diff --git a/README.md b/README.md index 604a9385..5169ecb8 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,6 @@ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fosdu-developer%2Fmain%2Fazuredeploy.json) - - [Open Subsurface Data Universe](https://osduforum.org) (OSDU) is a standard data platform that brings together a diverse array of subsurface and well data. It enables the energy industry to access and analyze data across various sources efficiently. This project aims to provide a streamlined approach for developing and working directly with [OSDU](https://community.opengroup.org/osdu/platform) using the [Azure Cloud Platform](https://azure.microsoft.com/). @@ -52,6 +49,19 @@ Once registered, refresh the Microsoft.ContainerService resource provider: az provider register --namespace Microsoft.ContainerService ``` + +## Templated Deployment + +The solution can be deployed directly with the ARM template but parameter options can be difficult to navigate. However, this method works just fine when leveraging a fully default deployment. + +[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fosdu-developer%2Fmain%2Fazuredeploy.json) + + + +## Azure Developer CLI - Workflow + +The recommended way for working with the solution is to leverage the Azure Developer CLI so that options can be better set, the solution modified or parameters changed in order to customize a deployment that has more flexability. + ### Enabling Alpha Features for Azure Developer CLI **Resource Group Scoped Deployments** @@ -99,9 +109,7 @@ APP_NAME= # <-- azd env set AZURE_CLIENT_ID $(az ad app list --display-name $APP_NAME --query "[].appId" -otsv) ``` - - -## Workspace +### Commands The solution template is provisioned using the azure developer cli. @@ -111,7 +119,7 @@ The solution template is provisioned using the azure developer cli. | Stop | `azd down --purge --force` | -### Infrastructure +## Infrastructure The following diagram repesents the infrastructure that is deployed by this solution. diff --git a/azuredeploy.json b/azuredeploy.json index dfeb31fe..ea0b25d7 100644 --- a/azuredeploy.json +++ b/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.24.24.22086", - "templateHash": "10434564249902073227" + "templateHash": "2557453613710039120" } }, "parameters": { @@ -30,6 +30,27 @@ "description": "Feature Flag to Enable a Pod Subnet" } }, + "virtualNetworkNewOrExisting": { + "type": "string", + "defaultValue": "new", + "metadata": { + "description": "Boolean indicating whether the VNet is new or existing" + } + }, + "virtualNetworkName": { + "type": "string", + "defaultValue": "osdu-network", + "metadata": { + "description": "Name of the Virtual Network (Optional: If exiting Network is selected)" + } + }, + "virtualNetworkResourceGroup": { + "type": "string", + "defaultValue": "osdu-network", + "metadata": { + "description": "Resource group of the VNet (Optional: If exiting Network is selected)" + } + }, "virtualNetworkAddressPrefix": { "type": "string", "defaultValue": "10.1.0.0/16", @@ -118,7 +139,7 @@ "type": "securestring", "defaultValue": "", "metadata": { - "description": "Feature Flag to Enable VPN Gateway Functionality" + "description": "Shared Key for VPN Gateway" } }, "remoteVpnPrefix": { @@ -135,6 +156,24 @@ "description": "IP Address Segment of the Remote Network" } }, + "enablePrivateLink": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Feature Flag to Enable Private Link" + } + }, + "cmekConfiguration": { + "type": "object", + "defaultValue": { + "kvUrl": "", + "keyName": "", + "identityId": "" + }, + "metadata": { + "description": "Optional. Customer Managed Encryption Key." + } + }, "applicationClientId": { "type": "string", "metadata": { @@ -147,6 +186,20 @@ "metadata": { "description": "Feature Flag to Enable Bastion" } + }, + "vmAdminUsername": { + "type": "string", + "defaultValue": "[if(parameters('enableBastion'), 'azureUser', newGuid())]", + "metadata": { + "description": "Specifies the name of the administrator account of the virtual machine." + } + }, + "vmAdminPasswordOrKey": { + "type": "securestring", + "defaultValue": "[if(parameters('enableBastion'), '', newGuid())]", + "metadata": { + "description": "Specifies the SSH Key or password for the virtual machine. SSH key is recommended." + } } }, "variables": { @@ -344,6 +397,21 @@ ] } } + }, + "vaultDNSZoneName": "privatelink.vaultcore.azure.net", + "storageDNSZoneForwarder": "[format('blob.{0}', environment().suffixes.storage)]", + "storageDnsZoneName": "[format('privatelink.{0}', variables('storageDNSZoneForwarder'))]", + "cosmosDnsZoneName": "privatelink.documents.azure.com", + "manageLayerConfig": { + "name": "manage", + "displayName": "Manage Resources", + "machine": { + "vmSize": "Standard_DS3_v2", + "imagePublisher": "Canonical", + "imageOffer": "UbuntuServer", + "imageSku": "18.04-LTS", + "authenticationType": "password" + } } }, "resources": [ @@ -368,6 +436,30 @@ "[resourceId('Microsoft.Network/virtualWans', format('{0}-wan', variables('commonLayerConfig').network.name))]" ] }, + { + "condition": "[parameters('enablePrivateLink')]", + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[variables('vaultDNSZoneName')]", + "location": "global", + "properties": {} + }, + { + "condition": "[parameters('enablePrivateLink')]", + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[variables('storageDnsZoneName')]", + "location": "global", + "properties": {} + }, + { + "condition": "[parameters('enablePrivateLink')]", + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[variables('cosmosDnsZoneName')]", + "location": "global", + "properties": {} + }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -6881,12 +6973,6726 @@ "[resourceId('Microsoft.Network/virtualHubs', format('{0}-hub', variables('commonLayerConfig').network.name))]", "[resourceId('Microsoft.Resources/deployments', format('{0}-vpn-site', variables('commonLayerConfig').name))]" ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-azure-keyvault', variables('commonLayerConfig').name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "resourceName": { + "value": "[variables('commonLayerConfig').name]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": { + "layer": "[variables('commonLayerConfig').displayName]" + } + }, + "diagnosticWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-log-analytics', variables('commonLayerConfig').name)), '2022-09-01').outputs.resourceId.value]" + }, + "diagnosticLogsRetentionInDays": { + "value": 0 + }, + "accessPolicies": { + "value": [ + { + "principalId": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-user-managed-identity', variables('commonLayerConfig').name)), '2022-09-01').outputs.principalId.value]", + "permissions": { + "secrets": [ + "get", + "list", + "set" + ], + "certificates": [ + "create", + "get", + "list" + ] + } + } + ] + }, + "secretsObject": { + "value": { + "secrets": [ + { + "secretName": "[variables('commonLayerConfig').secrets.tenantId]", + "secretValue": "[subscription().tenantId]" + }, + { + "secretName": "[variables('commonLayerConfig').secrets.subscriptionId]", + "secretValue": "[subscription().subscriptionId]" + }, + { + "secretName": "[variables('commonLayerConfig').secrets.clientId]", + "secretValue": "[parameters('applicationClientId')]" + }, + { + "secretName": "[variables('commonLayerConfig').secrets.applicationPrincipalId]", + "secretValue": "[parameters('applicationClientId')]" + }, + { + "secretName": "[variables('commonLayerConfig').secrets.stampIdentity]", + "secretValue": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-user-managed-identity', variables('commonLayerConfig').name)), '2022-09-01').outputs.principalId.value]" + } + ] + } + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principals": [ + { + "id": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-user-managed-identity', variables('commonLayerConfig').name)), '2022-09-01').outputs.principalId.value]", + "resourceId": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-user-managed-identity', variables('commonLayerConfig').name)), '2022-09-01').outputs.resourceId.value]" + } + ], + "principalType": "ServicePrincipal" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "16688316997286754234" + }, + "name": "Azure Key Vault", + "description": "This module deploys a key vault", + "owner": "azure-global-energy" + }, + "parameters": { + "resourceName": { + "type": "string", + "minLength": 3, + "maxLength": 20, + "metadata": { + "description": "Used to name all resources" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Resource Location." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Tags." + } + }, + "enableDeleteLock": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable lock to prevent accidental deletion" + } + }, + "enablePurgeProtection": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Provide 'true' to enable Key Vault's purge protection feature." + } + }, + "sku": { + "type": "string", + "defaultValue": "Standard", + "metadata": { + "description": "Key Vault SKU." + } + }, + "accessPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Specify Access Policies to Enable (Optional)." + } + }, + "softDeleteRetentionInDays": { + "type": "int", + "defaultValue": 7, + "minValue": 7, + "maxValue": 14, + "metadata": { + "description": "Key Vault Retention Days." + } + }, + "secretsObject": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Specifies all secrets {\"secretName\":\"\",\"secretValue\":\"\"} wrapped in a secure object." + } + }, + "crossTenant": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates if the module is used in a cross tenant scenario. If true, a resourceId must be provided in the role assignment's principal object." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of objects that describe RBAC permissions, format { roleDefinitionResourceId (string), principalId (string), principalType (enum), enabled (bool) }. Ref: https://docs.microsoft.com/en-us/azure/templates/microsoft.authorization/roleassignments?tabs=bicep" + } + }, + "diagnosticWorkspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace." + } + }, + "diagnosticStorageAccountId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account." + } + }, + "diagnosticEventHubAuthorizationRuleId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "diagnosticEventHubName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category." + } + }, + "diagnosticLogsRetentionInDays": { + "type": "int", + "defaultValue": 365, + "minValue": 0, + "maxValue": 365, + "metadata": { + "description": "Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely." + } + }, + "logsToEnable": { + "type": "array", + "defaultValue": [ + "AuditEvent", + "AzurePolicyEvaluationDetails" + ], + "allowedValues": [ + "AuditEvent", + "AzurePolicyEvaluationDetails" + ], + "metadata": { + "description": "Optional. The name of logs that will be streamed." + } + }, + "metricsToEnable": { + "type": "array", + "defaultValue": [ + "AllMetrics" + ], + "allowedValues": [ + "AllMetrics" + ], + "metadata": { + "description": "Optional. The name of metrics that will be streamed." + } + }, + "privateLinkSettings": { + "type": "object", + "defaultValue": { + "subnetId": "1", + "vnetId": "1" + }, + "metadata": { + "description": "Settings Required to Enable Private Link" + } + } + }, + "variables": { + "copy": [ + { + "name": "diagnosticsLogs", + "count": "[length(parameters('logsToEnable'))]", + "input": { + "category": "[parameters('logsToEnable')[copyIndex('diagnosticsLogs')]]", + "enabled": true, + "retentionPolicy": { + "enabled": true, + "days": "[parameters('diagnosticLogsRetentionInDays')]" + } + } + }, + { + "name": "diagnosticsMetrics", + "count": "[length(parameters('metricsToEnable'))]", + "input": { + "category": "[parameters('metricsToEnable')[copyIndex('diagnosticsMetrics')]]", + "timeGrain": null, + "enabled": true, + "retentionPolicy": { + "enabled": true, + "days": "[parameters('diagnosticLogsRetentionInDays')]" + } + } + } + ], + "name": "[format('kv-{0}{1}', replace(parameters('resourceName'), '-', ''), uniqueString(resourceGroup().id, parameters('resourceName')))]", + "enableSecrets": "[and(contains(parameters('secretsObject'), 'secrets'), greater(length(parameters('secretsObject').secrets), 0))]", + "enablePrivateLink": "[and(not(equals(parameters('privateLinkSettings').vnetId, '1')), not(equals(parameters('privateLinkSettings').subnetId, '1')))]", + "privateEndpointName": "[format('{0}-PrivateEndpoint', variables('name'))]", + "privateDNSZoneName": "privatelink.vaultcore.azure.net" + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "accessPolicies", + "count": "[length(parameters('accessPolicies'))]", + "input": { + "objectId": "[parameters('accessPolicies')[copyIndex('accessPolicies')].principalId]", + "tenantId": "[subscription().tenantId]", + "permissions": "[parameters('accessPolicies')[copyIndex('accessPolicies')].permissions]" + } + } + ], + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "[parameters('sku')]" + }, + "softDeleteRetentionInDays": "[parameters('softDeleteRetentionInDays')]", + "enabledForDeployment": false, + "enabledForDiskEncryption": true, + "enabledForTemplateDeployment": true, + "enableRbacAuthorization": false, + "enablePurgeProtection": "[if(parameters('enablePurgeProtection'), parameters('enablePurgeProtection'), null())]", + "networkAcls": "[if(variables('enablePrivateLink'), createObject('bypass', 'AzureServices', 'defaultAction', 'Deny'), createObject())]", + "publicNetworkAccess": "[if(variables('enablePrivateLink'), 'Disabled', 'Enabled')]" + } + }, + { + "copy": { + "name": "kv_secrets", + "count": "[length(parameters('secretsObject').secrets)]" + }, + "condition": "[variables('enableSecrets')]", + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2021-04-01-preview", + "name": "[format('{0}/{1}', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')), parameters('secretsObject').secrets[copyIndex()].secretName)]", + "properties": { + "value": "[parameters('secretsObject').secrets[copyIndex()].secretValue]" + }, + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + ] + }, + { + "condition": "[parameters('enableDeleteLock')]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2017-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]", + "name": "[format('{0}-lock', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]", + "properties": { + "level": "CanNotDelete" + }, + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + ] + }, + { + "condition": "[or(or(or(not(empty(parameters('diagnosticStorageAccountId'))), not(empty(parameters('diagnosticWorkspaceId')))), not(empty(parameters('diagnosticEventHubAuthorizationRuleId')))), not(empty(parameters('diagnosticEventHubName'))))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]", + "name": "keyvault-diagnostics", + "properties": { + "storageAccountId": "[if(not(empty(parameters('diagnosticStorageAccountId'))), parameters('diagnosticStorageAccountId'), null())]", + "workspaceId": "[if(not(empty(parameters('diagnosticWorkspaceId'))), parameters('diagnosticWorkspaceId'), null())]", + "eventHubAuthorizationRuleId": "[if(not(empty(parameters('diagnosticEventHubAuthorizationRuleId'))), parameters('diagnosticEventHubAuthorizationRuleId'), null())]", + "eventHubName": "[if(not(empty(parameters('diagnosticEventHubName'))), parameters('diagnosticEventHubName'), null())]", + "metrics": "[variables('diagnosticsMetrics')]", + "logs": "[variables('diagnosticsLogs')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + ] + }, + { + "condition": "[variables('enablePrivateLink')]", + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2021-02-01", + "name": "[variables('privateEndpointName')]", + "location": "[parameters('location')]", + "properties": { + "subnet": { + "id": "[parameters('privateLinkSettings').subnetId]" + }, + "privateLinkServiceConnections": [ + { + "name": "[variables('privateEndpointName')]", + "properties": { + "privateLinkServiceId": "[resourceId('Microsoft.KeyVault/vaults', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]", + "groupIds": [ + "vault" + ] + } + } + ], + "customDnsConfigs": [ + { + "fqdn": "[variables('privateDNSZoneName')]" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + ] + }, + { + "condition": "[variables('enablePrivateLink')]", + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}-link', variables('privateDNSZoneName'), variables('privateDNSZoneName'))]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[parameters('privateLinkSettings').vnetId]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', variables('privateDNSZoneName'))]" + ] + }, + { + "condition": "[variables('enablePrivateLink')]", + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[variables('privateDNSZoneName')]", + "location": "global" + }, + { + "condition": "[variables('enablePrivateLink')]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2022-01-01", + "name": "[format('{0}/dnsgroupname', variables('privateEndpointName'))]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "config1", + "properties": { + "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', variables('privateDNSZoneName'))]" + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', variables('privateDNSZoneName'))]", + "[resourceId('Microsoft.Network/privateEndpoints', variables('privateEndpointName'))]" + ] + }, + { + "copy": { + "name": "keyvault_rbac", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-rbac-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), createObject('value', parameters('roleAssignments')[copyIndex()].description), createObject('value', ''))]", + "principals": { + "value": "[parameters('roleAssignments')[copyIndex()].principals]" + }, + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "principalType": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), createObject('value', parameters('roleAssignments')[copyIndex()].principalType), createObject('value', ''))]", + "resourceId": { + "value": "[resourceId('Microsoft.KeyVault/vaults', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + }, + "crossTenant": { + "value": "[parameters('crossTenant')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "2893910713317644345" + } + }, + "parameters": { + "description": { + "type": "string", + "defaultValue": "" + }, + "principalType": { + "type": "string", + "defaultValue": "" + }, + "roleDefinitionIdOrName": { + "type": "string" + }, + "resourceId": { + "type": "string" + }, + "crossTenant": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates if the module is used in a cross tenant scenario. If true, a resourceId must be provided in the role assignment's principal object." + } + }, + "principals": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to. A resourceId is required when used in a cross tenant scenario (i.e. crossTenant is true)" + } + } + }, + "variables": { + "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', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6')]", + "Key Vault Crypto Service Encryption User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '')]", + "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')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principals'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(last(split(parameters('resourceId'), '/')), parameters('principals')[copyIndex()].id, parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principals')[copyIndex()].id]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "delegatedManagedIdentityResourceId": "[if(parameters('crossTenant'), parameters('principals')[copyIndex()].resourceId, null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the azure keyvault." + }, + "value": "[if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name'))]" + }, + "id": { + "type": "string", + "metadata": { + "description": "The resourceId of the azure keyvault." + }, + "value": "[resourceId('Microsoft.KeyVault/vaults', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-log-analytics', variables('commonLayerConfig').name))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-user-managed-identity', variables('commonLayerConfig').name))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-log-analytics-secrets', variables('commonLayerConfig').name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-azure-keyvault', variables('commonLayerConfig').name)), '2022-09-01').outputs.name.value]" + }, + "workspaceName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-log-analytics', variables('commonLayerConfig').name)), '2022-09-01').outputs.name.value]" + }, + "workspaceIdName": { + "value": "[variables('commonLayerConfig').secrets.logAnalyticsId]" + }, + "workspaceKeySecretName": { + "value": "[variables('commonLayerConfig').secrets.logAnalyticsKey]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.24.24.22086", + "templateHash": "870440921009018529" + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + } + }, + "workspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the Analytics Workspace. Required if the template is used in a standalone deployment." + } + }, + "workspaceIdName": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret." + } + }, + "workspaceKeySecretName": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret." + } + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('workspaceKeySecretName'))]", + "properties": { + "value": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspaceName')), '2021-06-01').primarySharedKey]" + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('workspaceIdName'))]", + "properties": { + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspaceName'))]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-azure-keyvault', variables('commonLayerConfig').name))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-log-analytics', variables('commonLayerConfig').name))]" + ] + }, + { + "condition": "[parameters('enableBastion')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-azure-keyvault-sshkey', variables('commonLayerConfig').name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "kvName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-azure-keyvault', variables('commonLayerConfig').name)), '2022-09-01').outputs.name.value]" + }, + "location": { + "value": "[parameters('location')]" + }, + "useExistingManagedIdentity": { + "value": true + }, + "managedIdentityName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-user-managed-identity', variables('commonLayerConfig').name)), '2022-09-01').outputs.name.value]" + }, + "existingManagedIdentitySubId": { + "value": "[subscription().subscriptionId]" + }, + "existingManagedIdentityResourceGroupName": { + "value": "[resourceGroup().name]" + }, + "sshKeyName": { + "value": "PrivateLinkSSHKey-" + }, + "cleanupPreference": { + "value": "Always" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.24.24.22086", + "templateHash": "14010778880958868816" + }, + "name": "SSH Key Pair", + "description": "This module creates a SSH Key Pair and stores it in an Azure Key Vault", + "owner": "azure-global-energy" + }, + "parameters": { + "kvName": { + "type": "string", + "metadata": { + "description": "The name of the Azure Key Vault" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location of the Key Vault and where to deploy the module resources to" + } + }, + "forceUpdateTag": { + "type": "string", + "defaultValue": "[utcNow()]", + "metadata": { + "description": "How the deployment script should be forced to execute" + } + }, + "rbacRoleNeeded": { + "type": "string", + "defaultValue": "b86a8fe4-44ce-4948-aee5-eccb2c155cd7", + "metadata": { + "description": "Azure RoleId that are required for the DeploymentScript resource to import images" + } + }, + "useExistingManagedIdentity": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Does the Managed Identity already exists, or should be created" + } + }, + "managedIdentityName": { + "type": "string", + "defaultValue": "[format('id-keyvault-ssh-{0}', parameters('location'))]", + "metadata": { + "description": "Name of the Managed Identity resource" + } + }, + "existingManagedIdentitySubId": { + "type": "string", + "defaultValue": "[subscription().subscriptionId]", + "metadata": { + "description": "For an existing Managed Identity, the Subscription Id it is located in" + } + }, + "existingManagedIdentityResourceGroupName": { + "type": "string", + "defaultValue": "[resourceGroup().name]", + "metadata": { + "description": "For an existing Managed Identity, the Resource Group it is located in" + } + }, + "sshKeyName": { + "type": "string", + "metadata": { + "description": "The name of the SSH Key to be created.\nif name is my-virtual-machine-ssh then the private key will be named my-virtual-machine-sshprivate and the public key will be named my-virtual-machine-sshpublic.\n" + } + }, + "initialScriptDelay": { + "type": "string", + "defaultValue": "30s", + "metadata": { + "description": "A delay before the script import operation starts. Primarily to allow Azure AAD Role Assignments to propagate" + } + }, + "cleanupPreference": { + "type": "string", + "defaultValue": "OnSuccess", + "allowedValues": [ + "OnSuccess", + "OnExpiration", + "Always" + ], + "metadata": { + "description": "When the script resource is cleaned up" + } + } + }, + "variables": { + "$fxv#0": "#!/bin/bash\nset -e\n\necho \"Waiting on Identity RBAC replication ($initialDelay)\"\nsleep $initialDelay\n\n# Generate the SSH key pair\necho \"Generating SSH key pair...\"\n#ssh-keygen -t rsa -b 4096 -C \"azure@example.com\" -f id_rsa -N \"\"\nssh-keygen -m PEM -t rsa -b 4096 -f id_rsa -q\n\n# Import the private key and public key as strings\nprivateKey=$(cat id_rsa)\npublicKey=$(cat id_rsa.pub)\n\n# Re-Login to Azure CLI using the managed identity\necho \"Logging in to Azure CLI using managed identity...\"\naz login --identity\n\necho \"Storing secret ${sshKeyName}private in Key Vault $keyVaultName...\"\nprivSecret=$(az keyvault secret set --vault-name \"$keyVaultName\" --name \"${sshKeyNamePrivate}\" --value \"$privateKey\")\n\necho \"Storing secret ${sshKeyName}public in Key Vault $keyVaultName...\"\npubSecret=$(az keyvault secret set --vault-name \"$keyVaultName\" --name \"${sshKeyNamePublic}\" --value \"$publicKey\")\n\nprivateSecretId=$(echo $privSecret | jq -r \".id\" | cut -d'/' -f-5) # remove the version from the url;\npublicSecretId=$(echo $pubSecret | jq -r \".id\" | cut -d'/' -f-5) # remove the version from the url;\n\njsonOutputString=$(jq -cn --arg public $publicSecretId --arg private $privateSecretId '{secretUris: $ARGS.named}')\necho $jsonOutputString\necho $jsonOutputString > $AZ_SCRIPTS_OUTPUT_PATH\n\n# Cleanup\nrm -f id_rsa id_rsa.pub", + "privateKeySecretName": "[format('{0}private', parameters('sshKeyName'))]", + "publicKeySecretName": "[format('{0}public', parameters('sshKeyName'))]" + }, + "resources": [ + { + "condition": "[not(parameters('useExistingManagedIdentity'))]", + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('managedIdentityName')]", + "location": "[parameters('location')]" + }, + { + "condition": "[not(empty(parameters('rbacRoleNeeded')))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('kvName'))]", + "name": "[guid(resourceId('Microsoft.KeyVault/vaults', parameters('kvName')), parameters('rbacRoleNeeded'), if(parameters('useExistingManagedIdentity'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))))]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('rbacRoleNeeded'))]", + "principalId": "[if(parameters('useExistingManagedIdentity'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), '2023-01-31').principalId, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), '2023-01-31').principalId)]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))]" + ] + }, + { + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2020-10-01", + "name": "[format('AKV-Cert-{0}-{1}', parameters('kvName'), replace(replace(parameters('sshKeyName'), ':', ''), '/', '-'))]", + "location": "[parameters('location')]", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', if(parameters('useExistingManagedIdentity'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))))]": {} + } + }, + "kind": "AzureCLI", + "properties": { + "forceUpdateTag": "[parameters('forceUpdateTag')]", + "azCliVersion": "2.45.0", + "timeout": "PT15M", + "retentionInterval": "PT1H", + "environmentVariables": [ + { + "name": "keyVaultName", + "value": "[parameters('kvName')]" + }, + { + "name": "sshKeyNamePrivate", + "value": "[variables('privateKeySecretName')]" + }, + { + "name": "sshKeyNamePublic", + "value": "[variables('publicKeySecretName')]" + }, + { + "name": "initialDelay", + "value": "[parameters('initialScriptDelay')]" + } + ], + "scriptContent": "[variables('$fxv#0')]", + "cleanupPreference": "[parameters('cleanupPreference')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))]", + "[extensionResourceId(resourceId('Microsoft.KeyVault/vaults', parameters('kvName')), 'Microsoft.Authorization/roleAssignments', guid(resourceId('Microsoft.KeyVault/vaults', parameters('kvName')), parameters('rbacRoleNeeded'), if(parameters('useExistingManagedIdentity'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')))))]" + ] + } + ], + "outputs": { + "publicKeyUri": { + "type": "string", + "metadata": { + "description": "The URI of the public key secret in the Key Vault" + }, + "value": "[reference(resourceId('Microsoft.Resources/deploymentScripts', format('AKV-Cert-{0}-{1}', parameters('kvName'), replace(replace(parameters('sshKeyName'), ':', ''), '/', '-'))), '2020-10-01').outputs.secretUris.public]" + }, + "privateKeyUri": { + "type": "string", + "metadata": { + "description": "The URI of the private key secret in the Key Vault" + }, + "value": "[reference(resourceId('Microsoft.Resources/deploymentScripts', format('AKV-Cert-{0}-{1}', parameters('kvName'), replace(replace(parameters('sshKeyName'), ':', ''), '/', '-'))), '2020-10-01').outputs.secretUris.private]" + }, + "publicKeySecretName": { + "type": "string", + "metadata": { + "description": "The name of the public key secret in the Key Vault" + }, + "value": "[variables('publicKeySecretName')]" + }, + "privateKeySecretName": { + "type": "string", + "metadata": { + "description": "The name of the private key secret in the Key Vault" + }, + "value": "[variables('privateKeySecretName')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-azure-keyvault', variables('commonLayerConfig').name))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-user-managed-identity', variables('commonLayerConfig').name))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-azure-keyvault-cert', variables('commonLayerConfig').name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "kvName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-azure-keyvault', variables('commonLayerConfig').name)), '2022-09-01').outputs.name.value]" + }, + "location": { + "value": "[parameters('location')]" + }, + "useExistingManagedIdentity": { + "value": true + }, + "managedIdentityName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-user-managed-identity', variables('commonLayerConfig').name)), '2022-09-01').outputs.name.value]" + }, + "existingManagedIdentitySubId": { + "value": "[subscription().subscriptionId]" + }, + "existingManagedIdentityResourceGroupName": { + "value": "[resourceGroup().name]" + }, + "certificateNames": { + "value": [ + "https-certificate" + ] + }, + "initialScriptDelay": { + "value": "0" + }, + "validity": { + "value": 24 + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.24.24.22086", + "templateHash": "1366021338178581650" + }, + "name": "Key Vault Certificate", + "description": "This module creates a Key Vault Certificate and stores it in an Azure Key Vault", + "owner": "azure-global-energy" + }, + "parameters": { + "kvName": { + "type": "string", + "metadata": { + "description": "The name of the Azure Key Vault" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "The location to deploy the resources to" + } + }, + "forceUpdateTag": { + "type": "string", + "defaultValue": "[utcNow()]", + "metadata": { + "description": "How the deployment script should be forced to execute" + } + }, + "rbacRolesNeededOnKV": { + "type": "string", + "defaultValue": "a4417e6f-fecd-4de8-b567-7b0420556985", + "metadata": { + "description": "The RoleDefinitionId required for the DeploymentScript resource to interact with KeyVault" + } + }, + "useExistingManagedIdentity": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Does the Managed Identity already exists, or should be created" + } + }, + "managedIdentityName": { + "type": "string", + "defaultValue": "[format('id-KeyVaultCertificateCreator-{0}', parameters('location'))]", + "metadata": { + "description": "Name of the Managed Identity resource" + } + }, + "existingManagedIdentitySubId": { + "type": "string", + "defaultValue": "[subscription().subscriptionId]", + "metadata": { + "description": "For an existing Managed Identity, the Subscription Id it is located in" + } + }, + "existingManagedIdentityResourceGroupName": { + "type": "string", + "defaultValue": "[resourceGroup().name]", + "metadata": { + "description": "For an existing Managed Identity, the Resource Group it is located in" + } + }, + "certificateNames": { + "type": "array", + "metadata": { + "description": "The names of the certificate to create. Use when creating many certificates." + } + }, + "certificateCommonNames": { + "type": "array", + "defaultValue": "[parameters('certificateNames')]", + "metadata": { + "description": "The common names of the certificate to create. Use when creating many certificates." + } + }, + "initialScriptDelay": { + "type": "string", + "defaultValue": "0", + "metadata": { + "description": "A delay before the script import operation starts. Primarily to allow Azure AAD Role Assignments to propagate" + } + }, + "cleanupPreference": { + "type": "string", + "defaultValue": "OnSuccess", + "allowedValues": [ + "OnSuccess", + "OnExpiration", + "Always" + ], + "metadata": { + "description": "When the script resource is cleaned up" + } + }, + "issuerName": { + "type": "string", + "defaultValue": "Self", + "metadata": { + "description": "Self, or user defined {IssuerName} for certificate signing" + } + }, + "issuerProvider": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Certificate Issuer Provider, DigiCert, GlobalSign, or internal options may be used." + } + }, + "disabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Create certificate in disabled state. Default: false" + } + }, + "accountId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Account ID of Certificate Issuer Account" + } + }, + "issuerPassword": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Password of Certificate Issuer Account" + } + }, + "organizationId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Organization ID of Certificate Issuer Account" + } + }, + "isCrossTenant": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Override this parameter if using this in cross tenant scenarios" + } + }, + "reuseKey": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "The default policy might cause errors about CSR being used before, so set this to false if that happens" + } + }, + "validity": { + "type": "int", + "defaultValue": 12, + "minValue": 1, + "maxValue": 1200, + "metadata": { + "description": "Optional. Override default validityInMonths 12 value" + } + }, + "performRoleAssignment": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Set to false to disable role assignments within this module. Default: true" + } + } + }, + "variables": { + "$fxv#0": "#!/bin/bash\nset -e\ninitialDelay=\"${initialDelay:-5}\"\nretryMax=\"${retryMax:-5}\"\ncertName=\"${certName:-default-cert}\"\ncertCommonName=\"${certCommonName:-default}\"\nvalidity=\"${validity:-12}\"\nakvName=\"${akvName:-keyvault}\"\nissuerName=\"${issuerName:-}\"\nreuseKey=\"${reuseKey:-true}\"\nretrySleep=\"${retrySleep:-5}\"\n\necho \"Waiting on Identity RBAC replication (\\\"$initialDelay\\\")\"\nsleep \"$initialDelay\"\n\n#Retry loop to catch errors (usually RBAC delays)\nretryLoopCount=0\nuntil [ \"$retryLoopCount\" -ge \"$retryMax\" ]\ndo\n echo \"Creating AKV Cert $certName with CN $certCommonName (attempt $retryLoopCount)...\"\n\n if [ -z \"$issuerName\" ] || [ -z \"$issuerProvider\" ]; then\n policy=$(az keyvault certificate get-default-policy \\\n | sed -e s/\\\"validityInMonths\\\":\\ 12/\\\"validityInMonths\\\":\\ \"${validity}\"/g \\\n | sed -e s/CN=CLIGetDefaultPolicy/CN=\"${certCommonName}\"/g )\n else\n if [ \"$issuerProvider\" == \"DigiCert\" ] || [ \"$issuerProvider\" == \"GlobalCert\" ]; then\n az keyvault certificate issuer create \\\n --vault-name \"$akvName\" \\\n --issuer-name \"$issuerName\" \\\n --provider-name \"$issuerProvider\" \\\n --account-id \"$accountId\" \\\n --password \"$issuerPassword\" \\\n --organizatiion-id \"$organizationId\"\n else\n az keyvault certificate issuer create \\\n --vault-name \"$akvName\" \\\n --issuer-name \"$issuerName\" \\\n --provider-name \"$issuerProvider\"\n fi\n policy=$(az keyvault certificate get-default-policy \\\n | sed -e s/\\\"validityInMonths\\\":\\ 12/\\\"validityInMonths\\\":\\ \"${validity}\"/g \\\n | sed -e s/CN=CLIGetDefaultPolicy/CN=\"${certCommonName}\"/g \\\n | sed -e s/\\\"name\\\":\\ \\\"Self\\\"/\\\"name\\\":\\ \\\"\"${issuerName}\"\\\"/g \\\n | sed -e s/\\\"reuseKey\\\":\\ true/\\\"reuseKey\\\":\\ \"${reuseKey}\"/g )\n fi\n az keyvault certificate create \\\n --vault-name \"$akvName\" \\\n -n \"$certName\" \\\n -p \"$policy\" \\\n --disabled \"$disabled\" \\\n && break\n\n sleep \"$retrySleep\"\n retryLoopCount=$((retryLoopCount+1))\ndone\n\necho \"Getting Certificate $certName\";\nretryLoopCount=0\ncreatedCert=$(az keyvault certificate show -n \"$certName\" --vault-name \"$akvName\" -o json)\nwhile [ -z \"$(echo \"$createdCert\" | jq -r '.x509ThumbprintHex')\" ] && [ $retryLoopCount -lt \"$retryMax\" ]\ndo\n echo \"Waiting for cert creation (attempt $retryLoopCount)...\"\n sleep $retrySleep\n createdCert=$(az keyvault certificate show -n $certName --vault-name $akvName -o json)\n retryLoopCount=$((retryLoopCount+1))\ndone\n\nunversionedSecretId=$(echo $createdCert | jq -r \".sid\" | cut -d'/' -f-5) # remove the version from the url;\njsonOutputString=$(echo $createdCert | jq --arg usid $unversionedSecretId '{name: .name ,certSecretId: {versioned: .sid, unversioned: $usid }, thumbprint: .x509Thumbprint, thumbprintHex: .x509ThumbprintHex}')\necho $jsonOutputString > $AZ_SCRIPTS_OUTPUT_PATH", + "delegatedManagedIdentityResourceId": "[if(parameters('useExistingManagedIdentity'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')))]" + }, + "resources": [ + { + "condition": "[not(parameters('useExistingManagedIdentity'))]", + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2018-11-30", + "name": "[parameters('managedIdentityName')]", + "location": "[parameters('location')]", + "metadata": { + "description": "A new managed identity that will be created in this Resource Group, this is the default option" + } + }, + { + "condition": "[parameters('performRoleAssignment')]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('kvName'))]", + "name": "[guid(resourceId('Microsoft.KeyVault/vaults', parameters('kvName')), parameters('rbacRolesNeededOnKV'), parameters('managedIdentityName'), string(parameters('useExistingManagedIdentity')))]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('rbacRolesNeededOnKV'))]", + "principalId": "[if(parameters('useExistingManagedIdentity'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), '2018-11-30').principalId, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), '2018-11-30').principalId)]", + "principalType": "ServicePrincipal", + "delegatedManagedIdentityResourceId": "[if(parameters('isCrossTenant'), variables('delegatedManagedIdentityResourceId'), null())]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))]" + ] + }, + { + "copy": { + "name": "createImportCerts", + "count": "[length(parameters('certificateNames'))]" + }, + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2020-10-01", + "name": "[format('AKV-Cert-{0}-{1}', parameters('kvName'), replace(replace(parameters('certificateNames')[copyIndex()], ':', ''), '/', '-'))]", + "location": "[parameters('location')]", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', if(parameters('useExistingManagedIdentity'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))))]": {} + } + }, + "kind": "AzureCLI", + "properties": { + "forceUpdateTag": "[parameters('forceUpdateTag')]", + "azCliVersion": "2.35.0", + "timeout": "PT10M", + "retentionInterval": "P1D", + "environmentVariables": [ + { + "name": "akvName", + "value": "[parameters('kvName')]" + }, + { + "name": "certName", + "value": "[parameters('certificateNames')[copyIndex()]]" + }, + { + "name": "certCommonName", + "value": "[parameters('certificateCommonNames')[copyIndex()]]" + }, + { + "name": "initialDelay", + "value": "[parameters('initialScriptDelay')]" + }, + { + "name": "issuerName", + "value": "[parameters('issuerName')]" + }, + { + "name": "issuerProvider", + "value": "[parameters('issuerProvider')]" + }, + { + "name": "disabled", + "value": "[toLower(string(parameters('disabled')))]" + }, + { + "name": "retryMax", + "value": "10" + }, + { + "name": "retrySleep", + "value": "5s" + }, + { + "name": "accountId", + "value": "[parameters('accountId')]" + }, + { + "name": "issuerPassword", + "secureValue": "[parameters('issuerPassword')]" + }, + { + "name": "organizationId", + "value": "[parameters('organizationId')]" + }, + { + "name": "reuseKey", + "value": "[toLower(string(parameters('reuseKey')))]" + }, + { + "name": "validity", + "value": "[string(parameters('validity'))]" + } + ], + "scriptContent": "[variables('$fxv#0')]", + "cleanupPreference": "[parameters('cleanupPreference')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))]", + "[extensionResourceId(resourceId('Microsoft.KeyVault/vaults', parameters('kvName')), 'Microsoft.Authorization/roleAssignments', guid(resourceId('Microsoft.KeyVault/vaults', parameters('kvName')), parameters('rbacRolesNeededOnKV'), parameters('managedIdentityName'), string(parameters('useExistingManagedIdentity'))))]" + ] + } + ], + "outputs": { + "certificateNames": { + "type": "array", + "metadata": { + "description": "Certificate names" + }, + "copy": { + "count": "[length(parameters('certificateNames'))]", + "input": "[createArray(reference(resourceId('Microsoft.Resources/deploymentScripts', format('AKV-Cert-{0}-{1}', parameters('kvName'), replace(replace(parameters('certificateNames')[copyIndex()], ':', ''), '/', '-'))), '2020-10-01').outputs.name)]" + } + }, + "certificateSecretIds": { + "type": "array", + "metadata": { + "description": "KeyVault secret ids to the created version" + }, + "copy": { + "count": "[length(parameters('certificateNames'))]", + "input": "[createArray(reference(resourceId('Microsoft.Resources/deploymentScripts', format('AKV-Cert-{0}-{1}', parameters('kvName'), replace(replace(parameters('certificateNames')[copyIndex()], ':', ''), '/', '-'))), '2020-10-01').outputs.certSecretId.versioned)]" + } + }, + "certificateSecretIdUnversioneds": { + "type": "array", + "metadata": { + "description": "KeyVault secret ids which uses the unversioned uri" + }, + "copy": { + "count": "[length(parameters('certificateNames'))]", + "input": "[createArray(reference(resourceId('Microsoft.Resources/deploymentScripts', format('AKV-Cert-{0}-{1}', parameters('kvName'), replace(replace(parameters('certificateNames')[copyIndex()], ':', ''), '/', '-'))), '2020-10-01').outputs.certSecretId.unversioned)]" + } + }, + "certificateThumbpints": { + "type": "array", + "metadata": { + "description": "Certificate Thumbprints" + }, + "copy": { + "count": "[length(parameters('certificateNames'))]", + "input": "[createArray(reference(resourceId('Microsoft.Resources/deploymentScripts', format('AKV-Cert-{0}-{1}', parameters('kvName'), replace(replace(parameters('certificateNames')[copyIndex()], ':', ''), '/', '-'))), '2020-10-01').outputs.thumbprint)]" + } + }, + "certificateThumbprintHexs": { + "type": "array", + "metadata": { + "description": "Certificate Thumbprints (in hex)" + }, + "copy": { + "count": "[length(parameters('certificateNames'))]", + "input": "[createArray(reference(resourceId('Microsoft.Resources/deploymentScripts', format('AKV-Cert-{0}-{1}', parameters('kvName'), replace(replace(parameters('certificateNames')[copyIndex()], ':', ''), '/', '-'))), '2020-10-01').outputs.thumbprintHex)]" + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-azure-keyvault', variables('commonLayerConfig').name))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-user-managed-identity', variables('commonLayerConfig').name))]" + ] + }, + { + "condition": "[parameters('enablePrivateLink')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-azure-keyvault-endpoint', variables('commonLayerConfig').name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "resourceName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-azure-keyvault', variables('commonLayerConfig').name)), '2022-09-01').outputs.name.value]" + }, + "subnetResourceId": { + "value": "[format('{0}/subnets/{1}', createObject('new', if(equals(parameters('virtualNetworkNewOrExisting'), 'new'), reference(resourceId('Microsoft.Resources/deployments', format('{0}-virtual-network', variables('commonLayerConfig').name)), '2022-09-01').outputs.resourceId.value, null()), 'existing', resourceId(parameters('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', parameters('virtualNetworkName')))[parameters('virtualNetworkNewOrExisting')], parameters('aksSubnetName'))]" + }, + "groupIds": { + "value": [ + "vault" + ] + }, + "privateDnsZoneGroup": { + "value": { + "privateDNSResourceIds": [ + "[resourceId('Microsoft.Network/privateDnsZones', variables('vaultDNSZoneName'))]" + ] + } + }, + "serviceResourceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-azure-keyvault', variables('commonLayerConfig').name)), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.12.40.16777", + "templateHash": "18408830660327806831" + } + }, + "parameters": { + "resourceName": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "serviceResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the resource that needs to be connected to the network." + } + }, + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. Subtype(s) of the connection to be created. The allowed values depend on the type serviceResourceId refers to." + } + }, + "applicationSecurityGroups": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "type": "object", + "defaultValue": {}, + "metadata": { + "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." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "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'." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Tags." + } + }, + "lock": { + "type": "string", + "defaultValue": "NotSpecified", + "metadata": { + "description": "Optional. Specify the type of lock." + }, + "allowedValues": [ + "CanNotDelete", + "NotSpecified", + "ReadOnly" + ] + }, + "customDnsConfigs": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Manual PrivateLink Service Connections." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2022-05-01", + "name": "[parameters('resourceName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "applicationSecurityGroups": "[parameters('applicationSecurityGroups')]", + "customNetworkInterfaceName": "[parameters('customNetworkInterfaceName')]", + "ipConfigurations": "[parameters('ipConfigurations')]", + "manualPrivateLinkServiceConnections": "[parameters('manualPrivateLinkServiceConnections')]", + "customDnsConfigs": "[parameters('customDnsConfigs')]", + "privateLinkServiceConnections": [ + { + "name": "[parameters('resourceName')]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceId')]", + "groupIds": "[parameters('groupIds')]" + } + } + ], + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + { + "condition": "[not(equals(parameters('lock'), 'NotSpecified'))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2017-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('resourceName'))]", + "name": "[format('{0}-{1}-lock', parameters('resourceName'), parameters('lock'))]", + "properties": { + "level": "[parameters('lock')]", + "notes": "[if(equals(parameters('lock'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName'))]" + ] + }, + { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-{1}', deployment().name, parameters('resourceName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDNSResourceIds": { + "value": "[parameters('privateDnsZoneGroup').privateDNSResourceIds]" + }, + "privateEndpointName": { + "value": "[parameters('resourceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.12.40.16777", + "templateHash": "2962080553890533733" + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "maxLength": 5, + "minLength": 1, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + } + }, + "resourceName": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2022-05-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('resourceName'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "value": "[resourceGroup().name]", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + } + }, + "name": { + "type": "string", + "value": "[parameters('resourceName')]", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + } + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('resourceName'))]", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName'))]" + ] + }, + { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-rbac-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": { + "value": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), parameters('roleAssignments')[copyIndex()].description, '')]" + }, + "principalIds": { + "value": "[parameters('roleAssignments')[copyIndex()].principalIds]" + }, + "principalType": { + "value": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), parameters('roleAssignments')[copyIndex()].principalType, '')]" + }, + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "condition": { + "value": "[if(contains(parameters('roleAssignments')[copyIndex()], 'condition'), parameters('roleAssignments')[copyIndex()].condition, '')]" + }, + "delegatedManagedIdentityResourceId": { + "value": "[if(contains(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), parameters('roleAssignments')[copyIndex()].delegatedManagedIdentityResourceId, '')]" + }, + "resourceId": { + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.12.40.16777", + "templateHash": "17398245239701469576" + } + }, + "parameters": { + "principalIds": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource to apply the role assignment to." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"" + } + }, + "conditionVersion": { + "type": "string", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Id of the delegated managed identity resource." + } + } + }, + "variables": { + "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')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principalIds'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', last(split(parameters('resourceId'), '/'))), parameters('principalIds')[copyIndex()], parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principalIds')[copyIndex()]]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName'))]" + ] + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "value": "[resourceGroup().name]", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + } + }, + "name": { + "type": "string", + "value": "[parameters('resourceName')]", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName'))]", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "location": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName')), '2022-05-01', 'full').location]", + "metadata": { + "description": "The location the resource was deployed into." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-azure-keyvault', variables('commonLayerConfig').name))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-virtual-network', variables('commonLayerConfig').name))]", + "[resourceId('Microsoft.Network/privateDnsZones', variables('vaultDNSZoneName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-azure-storage', variables('commonLayerConfig').name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "resourceName": { + "value": "[variables('commonLayerConfig').name]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": { + "layer": "[variables('commonLayerConfig').displayName]" + } + }, + "diagnosticWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-log-analytics', variables('commonLayerConfig').name)), '2022-09-01').outputs.resourceId.value]" + }, + "diagnosticLogsRetentionInDays": { + "value": 0 + }, + "sku": { + "value": "[variables('commonLayerConfig').storage.sku]" + }, + "tables": { + "value": "[variables('commonLayerConfig').storage.tables]" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Contributor", + "principals": [ + { + "id": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-user-managed-identity', variables('commonLayerConfig').name)), '2022-09-01').outputs.principalId.value]", + "resourceId": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-user-managed-identity', variables('commonLayerConfig').name)), '2022-09-01').outputs.resourceId.value]" + } + ], + "principalType": "ServicePrincipal" + } + ] + }, + "cmekConfiguration": { + "value": "[parameters('cmekConfiguration')]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-azure-keyvault', variables('commonLayerConfig').name)), '2022-09-01').outputs.name.value]" + }, + "storageAccountSecretName": { + "value": "[variables('commonLayerConfig').secrets.storageAccountName]" + }, + "storageAccountKeySecretName": { + "value": "[variables('commonLayerConfig').secrets.storageAccountKey]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.13.1.58284", + "templateHash": "10295457327684038729" + } + }, + "parameters": { + "resourceName": { + "type": "string", + "metadata": { + "description": "Used to name all resources" + }, + "maxLength": 22, + "minLength": 3 + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Resource Location." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Tags." + } + }, + "lock": { + "type": "string", + "defaultValue": "NotSpecified", + "metadata": { + "description": "Optional. Specify the type of lock." + }, + "allowedValues": [ + "CanNotDelete", + "NotSpecified", + "ReadOnly" + ] + }, + "sku": { + "type": "string", + "defaultValue": "Standard_LRS", + "allowedValues": [ + "Standard_LRS", + "Premium_LRS", + "Standard_GRS" + ], + "metadata": { + "description": "Specifies the storage account sku type." + } + }, + "accessTier": { + "type": "string", + "defaultValue": "Hot", + "allowedValues": [ + "Cool", + "Hot" + ], + "metadata": { + "description": "Specifies the storage account access tier." + } + }, + "containers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of Storage Containers to be created." + } + }, + "tables": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of Storage Tables to be created." + } + }, + "crossTenant": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates if the module is used in a cross tenant scenario. If true, a resourceId must be provided in the role assignment's principal object." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of objects that describe RBAC permissions, format { roleDefinitionResourceId (string), principalId (string), principalType (enum), enabled (bool) }. Ref: https://docs.microsoft.com/en-us/azure/templates/microsoft.authorization/roleassignments?tabs=bicep" + } + }, + "diagnosticWorkspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace." + } + }, + "diagnosticStorageAccountId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account." + } + }, + "diagnosticEventHubAuthorizationRuleId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "diagnosticEventHubName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category." + } + }, + "diagnosticLogsRetentionInDays": { + "type": "int", + "defaultValue": 365, + "maxValue": 365, + "minValue": 0, + "metadata": { + "description": "Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely." + } + }, + "logsToEnable": { + "type": "array", + "defaultValue": [ + "StorageRead", + "StorageWrite", + "StorageDelete" + ], + "allowedValues": [ + "StorageRead", + "StorageWrite", + "StorageDelete" + ], + "metadata": { + "description": "Optional. The name of logs that will be streamed." + } + }, + "metricsToEnable": { + "type": "array", + "defaultValue": [ + "AllMetrics" + ], + "allowedValues": [ + "AllMetrics" + ], + "metadata": { + "description": "Optional. The name of metrics that will be streamed." + } + }, + "cmekConfiguration": { + "type": "object", + "defaultValue": { + "kvUrl": "", + "keyName": "", + "identityId": "" + }, + "metadata": { + "description": "Optional. Customer Managed Encryption Key." + } + }, + "deleteRetention": { + "type": "int", + "defaultValue": 0, + "maxValue": 365, + "minValue": 0, + "metadata": { + "description": "Amount of days the soft deleted data is stored and available for recovery. 0 is off." + } + }, + "privateLinkSettings": { + "type": "object", + "defaultValue": { + "subnetId": "1", + "vnetId": "1" + }, + "metadata": { + "description": "Settings Required to Enable Private Link" + } + }, + "keyVaultName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional: Key Vault Name to store secrets into" + } + }, + "storageAccountSecretName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional: To save storage account name into vault set the secret hame." + } + }, + "storageAccountKeySecretName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional: To save storage account key into vault set the secret hame." + } + }, + "storageAccountConnectionString": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional: To save storage account connectionstring into vault set the secret hame." + } + } + }, + "variables": { + "copy": [ + { + "name": "diagnosticsLogs", + "count": "[length(parameters('logsToEnable'))]", + "input": { + "category": "[parameters('logsToEnable')[copyIndex('diagnosticsLogs')]]", + "enabled": true, + "retentionPolicy": { + "enabled": true, + "days": "[parameters('diagnosticLogsRetentionInDays')]" + } + } + }, + { + "name": "diagnosticsMetrics", + "count": "[length(parameters('metricsToEnable'))]", + "input": { + "category": "[parameters('metricsToEnable')[copyIndex('diagnosticsMetrics')]]", + "timeGrain": null, + "enabled": true, + "retentionPolicy": { + "enabled": true, + "days": "[parameters('diagnosticLogsRetentionInDays')]" + } + } + } + ], + "enableCMEK": "[if(and(and(not(empty(parameters('cmekConfiguration').kvUrl)), not(empty(parameters('cmekConfiguration').keyName))), not(empty(parameters('cmekConfiguration').identityId))), true(), false())]", + "name": "[format('sa{0}{1}', replace(parameters('resourceName'), '-', ''), uniqueString(resourceGroup().id, parameters('resourceName')))]", + "enablePrivateLink": "[and(not(equals(parameters('privateLinkSettings').vnetId, '1')), not(equals(parameters('privateLinkSettings').subnetId, '1')))]", + "privateEndpointName": "[format('{0}-PrivateEndpoint', variables('name'))]", + "publicDNSZoneForwarder": "[format('blob.{0}', environment().suffixes.storage)]", + "privateDnsZoneName": "[format('privatelink.{0}', variables('publicDNSZoneForwarder'))]" + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-05-01", + "name": "[if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]" + }, + "kind": "StorageV2", + "identity": "[if(variables('enableCMEK'), createObject('type', 'UserAssigned', 'userAssignedIdentities', createObject(format('{0}', parameters('cmekConfiguration').identityId), createObject())), json('null'))]", + "properties": { + "accessTier": "[parameters('accessTier')]", + "minimumTlsVersion": "TLS1_2", + "encryption": "[if(variables('enableCMEK'), createObject('identity', createObject('userAssignedIdentity', parameters('cmekConfiguration').identityId), 'services', createObject('blob', createObject('enabled', true()), 'table', createObject('enabled', true()), 'file', createObject('enabled', true())), 'keySource', 'Microsoft.Keyvault', 'keyvaultproperties', createObject('keyname', parameters('cmekConfiguration').keyName, 'keyvaulturi', parameters('cmekConfiguration').kvUrl)), createObject('services', createObject('blob', createObject('enabled', true()), 'table', createObject('enabled', true()), 'file', createObject('enabled', true())), 'keySource', 'Microsoft.Storage'))]", + "networkAcls": "[if(variables('enablePrivateLink'), createObject('bypass', 'AzureServices', 'defaultAction', 'Deny'), createObject())]" + } + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2021-04-01", + "name": "[format('{0}/{1}', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')), 'default')]", + "properties": "[if(greater(parameters('deleteRetention'), 0), createObject('changeFeed', createObject('enabled', true()), 'restorePolicy', createObject('enabled', true(), 'days', 6), 'isVersioningEnabled', true(), 'deleteRetentionPolicy', createObject('enabled', true(), 'days', parameters('deleteRetention'))), createObject('deleteRetentionPolicy', createObject('enabled', false(), 'allowPermanentDelete', false())))]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + ] + }, + { + "copy": { + "name": "storage_containers", + "count": "[length(parameters('containers'))]" + }, + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2022-05-01", + "name": "[format('{0}/default/{1}', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')), parameters('containers')[copyIndex()])]", + "properties": { + "defaultEncryptionScope": "$account-encryption-key", + "denyEncryptionScopeOverride": false, + "publicAccess": "None" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + ] + }, + { + "copy": { + "name": "storage_tables", + "count": "[length(parameters('tables'))]" + }, + "type": "Microsoft.Storage/storageAccounts/tableServices/tables", + "apiVersion": "2022-05-01", + "name": "[format('{0}/default/{1}', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')), parameters('tables')[copyIndex()])]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + ] + }, + { + "condition": "[not(equals(parameters('lock'), 'NotSpecified'))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2017-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]", + "name": "[format('{0}-{1}-lock', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')), parameters('lock'))]", + "properties": { + "level": "[parameters('lock')]", + "notes": "[if(equals(parameters('lock'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + ] + }, + { + "condition": "[or(or(or(not(empty(parameters('diagnosticStorageAccountId'))), not(empty(parameters('diagnosticWorkspaceId')))), not(empty(parameters('diagnosticEventHubAuthorizationRuleId')))), not(empty(parameters('diagnosticEventHubName'))))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')), 'default')]", + "name": "storage-diagnostics", + "properties": { + "storageAccountId": "[if(not(empty(parameters('diagnosticStorageAccountId'))), parameters('diagnosticStorageAccountId'), null())]", + "workspaceId": "[if(not(empty(parameters('diagnosticWorkspaceId'))), parameters('diagnosticWorkspaceId'), null())]", + "eventHubAuthorizationRuleId": "[if(not(empty(parameters('diagnosticEventHubAuthorizationRuleId'))), parameters('diagnosticEventHubAuthorizationRuleId'), null())]", + "eventHubName": "[if(not(empty(parameters('diagnosticEventHubName'))), parameters('diagnosticEventHubName'), null())]", + "metrics": "[variables('diagnosticsMetrics')]", + "logs": "[variables('diagnosticsLogs')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')), 'default')]", + "[resourceId('Microsoft.Storage/storageAccounts', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + ] + }, + { + "condition": "[variables('enablePrivateLink')]", + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[variables('privateDnsZoneName')]", + "location": "global", + "properties": {} + }, + { + "condition": "[variables('enablePrivateLink')]", + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2022-01-01", + "name": "[variables('privateEndpointName')]", + "location": "[parameters('location')]", + "properties": { + "privateLinkServiceConnections": [ + { + "name": "[variables('privateEndpointName')]", + "properties": { + "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]", + "groupIds": [ + "blob" + ] + } + } + ], + "subnet": { + "id": "[parameters('privateLinkSettings').subnetId]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + ] + }, + { + "condition": "[variables('enablePrivateLink')]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2022-01-01", + "name": "[format('{0}/dnsgroupname', variables('privateEndpointName'))]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "dnsConfig", + "properties": { + "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', variables('privateDnsZoneName'))]" + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', variables('privateDnsZoneName'))]", + "[resourceId('Microsoft.Network/privateEndpoints', variables('privateEndpointName'))]" + ] + }, + { + "condition": "[variables('enablePrivateLink')]", + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', variables('privateDnsZoneName'), 'link_to_vnet')]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[parameters('privateLinkSettings').vnetId]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', variables('privateDnsZoneName'))]" + ] + }, + { + "copy": { + "name": "storage_rbac", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-rbac-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), createObject('value', parameters('roleAssignments')[copyIndex()].description), createObject('value', ''))]", + "principals": { + "value": "[parameters('roleAssignments')[copyIndex()].principals]" + }, + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "principalType": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), createObject('value', parameters('roleAssignments')[copyIndex()].principalType), createObject('value', ''))]", + "resourceId": { + "value": "[resourceId('Microsoft.Storage/storageAccounts', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + }, + "crossTenant": { + "value": "[parameters('crossTenant')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.13.1.58284", + "templateHash": "5790547434381822001" + } + }, + "parameters": { + "description": { + "type": "string", + "defaultValue": "" + }, + "principalType": { + "type": "string", + "defaultValue": "" + }, + "roleDefinitionIdOrName": { + "type": "string" + }, + "resourceId": { + "type": "string" + }, + "crossTenant": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates if the module is used in a cross tenant scenario. If true, a resourceId must be provided in the role assignment's principal object." + } + }, + "principals": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to. A resourceId is required when used in a cross tenant scenario (i.e. crossTenant is true)" + } + } + }, + "variables": { + "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')]", + "Backup Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a795c7a0-d4a2-40c1-ae25-d81f01202912')]", + "Classic Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '')]", + "Classic Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '')]", + "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 Lake Analytics Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88')]", + "Elastic SAN Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '80dcbedb-47ef-405d-95bd-188a1b4ac406')]", + "Elastic SAN Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'af6a70f8-3c9f-4105-acf1-d719e9fca4ca')]", + "Elastic SAN Volume Group Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8281131-f312-4f34-8d98-ae12be9f0d23')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", + "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', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", + "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')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principals'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(last(split(parameters('resourceId'), '/')), parameters('principals')[copyIndex()].id, parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principals')[copyIndex()].id]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "delegatedManagedIdentityResourceId": "[if(parameters('crossTenant'), parameters('principals')[copyIndex()].resourceId, null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + ] + }, + { + "condition": "[and(not(empty(parameters('keyVaultName'))), not(empty(parameters('storageAccountSecretName'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-secret-name', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "name": { + "value": "[parameters('storageAccountSecretName')]" + }, + "value": { + "value": "[if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.13.1.58284", + "templateHash": "16107441205299703534" + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret." + } + }, + "value": { + "type": "secureString", + "metadata": { + "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." + } + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "value": "[parameters('value')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('name')]", + "metadata": { + "description": "The name of the secret." + } + }, + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('name'))]", + "metadata": { + "description": "The resource ID of the secret." + } + }, + "resourceGroupName": { + "type": "string", + "value": "[resourceGroup().name]", + "metadata": { + "description": "The name of the resource group the secret was created in." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + ] + }, + { + "condition": "[and(not(empty(parameters('keyVaultName'))), not(empty(parameters('storageAccountKeySecretName'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-secret-key', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "name": { + "value": "[parameters('storageAccountKeySecretName')]" + }, + "value": { + "value": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name'))), '2022-05-01').keys[0].value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.13.1.58284", + "templateHash": "16107441205299703534" + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret." + } + }, + "value": { + "type": "secureString", + "metadata": { + "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." + } + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "value": "[parameters('value')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('name')]", + "metadata": { + "description": "The name of the secret." + } + }, + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('name'))]", + "metadata": { + "description": "The resource ID of the secret." + } + }, + "resourceGroupName": { + "type": "string", + "value": "[resourceGroup().name]", + "metadata": { + "description": "The name of the resource group the secret was created in." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + ] + }, + { + "condition": "[and(not(empty(parameters('keyVaultName'))), not(empty(parameters('storageAccountConnectionString'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-secret-accountName', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "name": { + "value": "[parameters('storageAccountConnectionString')]" + }, + "value": { + "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')), listKeys(resourceId('Microsoft.Storage/storageAccounts', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name'))), '2022-05-01').keys[0].value, environment().suffixes.storage)]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.13.1.58284", + "templateHash": "16107441205299703534" + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret." + } + }, + "value": { + "type": "secureString", + "metadata": { + "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." + } + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "value": "[parameters('value')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('name')]", + "metadata": { + "description": "The name of the secret." + } + }, + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('name'))]", + "metadata": { + "description": "The resource ID of the secret." + } + }, + "resourceGroupName": { + "type": "string", + "value": "[resourceGroup().name]", + "metadata": { + "description": "The name of the resource group the secret was created in." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]" + ] + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Storage/storageAccounts', if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name')))]", + "metadata": { + "description": "The resource ID." + } + }, + "name": { + "type": "string", + "value": "[if(greater(length(variables('name')), 24), substring(variables('name'), 0, 24), variables('name'))]", + "metadata": { + "description": "The name of the resource." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-azure-keyvault', variables('commonLayerConfig').name))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-log-analytics', variables('commonLayerConfig').name))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-user-managed-identity', variables('commonLayerConfig').name))]" + ] + }, + { + "condition": "[parameters('enablePrivateLink')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-azure-storage-endpoint', variables('commonLayerConfig').name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "resourceName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-azure-storage', variables('commonLayerConfig').name)), '2022-09-01').outputs.name.value]" + }, + "subnetResourceId": { + "value": "[format('{0}/subnets/{1}', createObject('new', if(equals(parameters('virtualNetworkNewOrExisting'), 'new'), reference(resourceId('Microsoft.Resources/deployments', format('{0}-virtual-network', variables('commonLayerConfig').name)), '2022-09-01').outputs.resourceId.value, null()), 'existing', resourceId(parameters('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', parameters('virtualNetworkName')))[parameters('virtualNetworkNewOrExisting')], parameters('aksSubnetName'))]" + }, + "serviceResourceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-azure-storage', variables('commonLayerConfig').name)), '2022-09-01').outputs.id.value]" + }, + "groupIds": { + "value": [ + "blob" + ] + }, + "privateDnsZoneGroup": { + "value": { + "privateDNSResourceIds": [ + "[resourceId('Microsoft.Network/privateDnsZones', variables('storageDnsZoneName'))]" + ] + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.12.40.16777", + "templateHash": "18408830660327806831" + } + }, + "parameters": { + "resourceName": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "serviceResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the resource that needs to be connected to the network." + } + }, + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. Subtype(s) of the connection to be created. The allowed values depend on the type serviceResourceId refers to." + } + }, + "applicationSecurityGroups": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "type": "object", + "defaultValue": {}, + "metadata": { + "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." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "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'." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Tags." + } + }, + "lock": { + "type": "string", + "defaultValue": "NotSpecified", + "metadata": { + "description": "Optional. Specify the type of lock." + }, + "allowedValues": [ + "CanNotDelete", + "NotSpecified", + "ReadOnly" + ] + }, + "customDnsConfigs": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Manual PrivateLink Service Connections." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2022-05-01", + "name": "[parameters('resourceName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "applicationSecurityGroups": "[parameters('applicationSecurityGroups')]", + "customNetworkInterfaceName": "[parameters('customNetworkInterfaceName')]", + "ipConfigurations": "[parameters('ipConfigurations')]", + "manualPrivateLinkServiceConnections": "[parameters('manualPrivateLinkServiceConnections')]", + "customDnsConfigs": "[parameters('customDnsConfigs')]", + "privateLinkServiceConnections": [ + { + "name": "[parameters('resourceName')]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceId')]", + "groupIds": "[parameters('groupIds')]" + } + } + ], + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + { + "condition": "[not(equals(parameters('lock'), 'NotSpecified'))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2017-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('resourceName'))]", + "name": "[format('{0}-{1}-lock', parameters('resourceName'), parameters('lock'))]", + "properties": { + "level": "[parameters('lock')]", + "notes": "[if(equals(parameters('lock'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName'))]" + ] + }, + { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-{1}', deployment().name, parameters('resourceName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDNSResourceIds": { + "value": "[parameters('privateDnsZoneGroup').privateDNSResourceIds]" + }, + "privateEndpointName": { + "value": "[parameters('resourceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.12.40.16777", + "templateHash": "2962080553890533733" + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "maxLength": 5, + "minLength": 1, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + } + }, + "resourceName": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2022-05-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('resourceName'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "value": "[resourceGroup().name]", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + } + }, + "name": { + "type": "string", + "value": "[parameters('resourceName')]", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + } + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('resourceName'))]", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName'))]" + ] + }, + { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-rbac-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": { + "value": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), parameters('roleAssignments')[copyIndex()].description, '')]" + }, + "principalIds": { + "value": "[parameters('roleAssignments')[copyIndex()].principalIds]" + }, + "principalType": { + "value": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), parameters('roleAssignments')[copyIndex()].principalType, '')]" + }, + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "condition": { + "value": "[if(contains(parameters('roleAssignments')[copyIndex()], 'condition'), parameters('roleAssignments')[copyIndex()].condition, '')]" + }, + "delegatedManagedIdentityResourceId": { + "value": "[if(contains(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), parameters('roleAssignments')[copyIndex()].delegatedManagedIdentityResourceId, '')]" + }, + "resourceId": { + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.12.40.16777", + "templateHash": "17398245239701469576" + } + }, + "parameters": { + "principalIds": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource to apply the role assignment to." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"" + } + }, + "conditionVersion": { + "type": "string", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Id of the delegated managed identity resource." + } + } + }, + "variables": { + "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')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principalIds'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', last(split(parameters('resourceId'), '/'))), parameters('principalIds')[copyIndex()], parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principalIds')[copyIndex()]]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName'))]" + ] + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "value": "[resourceGroup().name]", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + } + }, + "name": { + "type": "string", + "value": "[parameters('resourceName')]", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName'))]", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "location": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName')), '2022-05-01', 'full').location]", + "metadata": { + "description": "The location the resource was deployed into." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-azure-storage', variables('commonLayerConfig').name))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-virtual-network', variables('commonLayerConfig').name))]", + "[resourceId('Microsoft.Network/privateDnsZones', variables('storageDnsZoneName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-cosmos-db', variables('commonLayerConfig').name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "resourceName": { + "value": "[variables('commonLayerConfig').name]" + }, + "resourceLocation": { + "value": "[parameters('location')]" + }, + "tags": { + "value": { + "layer": "[variables('commonLayerConfig').displayName]" + } + }, + "diagnosticWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-log-analytics', variables('commonLayerConfig').name)), '2022-09-01').outputs.resourceId.value]" + }, + "diagnosticLogsRetentionInDays": { + "value": 0 + }, + "capabilitiesToAdd": { + "value": [ + "EnableGremlin" + ] + }, + "gremlinDatabases": { + "value": [ + { + "name": "[variables('commonLayerConfig').database.name]", + "graphs": "[variables('commonLayerConfig').database.graphs]" + } + ] + }, + "throughput": { + "value": "[variables('commonLayerConfig').database.throughput]" + }, + "backupPolicyType": { + "value": "[variables('commonLayerConfig').database.backup]" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Contributor", + "principals": [ + { + "id": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-user-managed-identity', variables('commonLayerConfig').name)), '2022-09-01').outputs.principalId.value]", + "resourceId": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-user-managed-identity', variables('commonLayerConfig').name)), '2022-09-01').outputs.resourceId.value]" + } + ], + "principalType": "ServicePrincipal" + } + ] + }, + "systemAssignedIdentity": { + "value": false + }, + "userAssignedIdentities": "[if(not(empty(parameters('cmekConfiguration').identityId)), createObject('value', createObject(format('{0}', reference(resourceId('Microsoft.Resources/deployments', format('{0}-user-managed-identity', variables('commonLayerConfig').name)), '2022-09-01').outputs.resourceId.value), createObject(), format('{0}', parameters('cmekConfiguration').identityId), createObject())), createObject('value', createObject(format('{0}', reference(resourceId('Microsoft.Resources/deployments', format('{0}-user-managed-identity', variables('commonLayerConfig').name)), '2022-09-01').outputs.resourceId.value), createObject())))]", + "defaultIdentity": "[if(not(empty(parameters('cmekConfiguration').identityId)), createObject('value', parameters('cmekConfiguration').identityId), createObject('value', ''))]", + "kvKeyUri": "[if(and(not(empty(parameters('cmekConfiguration').kvUrl)), not(empty(parameters('cmekConfiguration').keyName))), createObject('value', format('{0}/keys/{1}', parameters('cmekConfiguration').kvUrl, parameters('cmekConfiguration').keyName)), createObject('value', ''))]", + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-azure-keyvault', variables('commonLayerConfig').name)), '2022-09-01').outputs.name.value]" + }, + "databaseEndpointSecretName": { + "value": "[variables('commonLayerConfig').secrets.cosmosEndpoint]" + }, + "databasePrimaryKeySecretName": { + "value": "[variables('commonLayerConfig').secrets.cosmosPrimaryKey]" + }, + "databaseConnectionStringSecretName": { + "value": "[variables('commonLayerConfig').secrets.cosmosConnectionString]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.13.1.58284", + "templateHash": "16371637483759106991" + } + }, + "parameters": { + "resourceName": { + "type": "string", + "metadata": { + "description": "Used to name all resources" + }, + "maxLength": 20, + "minLength": 3 + }, + "resourceLocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional: Resource Location." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Tags." + } + }, + "enableDeleteLock": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable lock to prevent accidental deletion" + } + }, + "multiwriteRegions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Locations enabled for the Cosmos DB account." + } + }, + "maxThroughput": { + "type": "int", + "defaultValue": 4000, + "metadata": { + "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." + } + }, + "throughput": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Request Units per second (for example 10000). Cannot be set together with `maxThroughput`." + } + }, + "systemAssignedIdentity": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedIdentities": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The ID(s) to assign to the resource." + } + }, + "defaultIdentity": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The default identity to be used." + } + }, + "databaseAccountOfferType": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Standard" + ], + "metadata": { + "description": "Optional. The offer type for the Cosmos DB database account." + } + }, + "defaultConsistencyLevel": { + "type": "string", + "defaultValue": "Session", + "metadata": { + "description": "Optional. The default consistency level of the Cosmos DB account." + }, + "allowedValues": [ + "Eventual", + "ConsistentPrefix", + "Session", + "BoundedStaleness", + "Strong" + ] + }, + "automaticFailover": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable automatic failover for regions." + } + }, + "maxStalenessPrefix": { + "type": "int", + "defaultValue": 100000, + "metadata": { + "description": "Optional. Max stale requests. Required for BoundedStaleness. Valid ranges, Single Region: 10 to 1000000. Multi Region: 100000 to 1000000." + }, + "maxValue": 2147483647, + "minValue": 10 + }, + "maxIntervalInSeconds": { + "type": "int", + "defaultValue": 300, + "metadata": { + "description": "Optional. Max lag time (minutes). Required for BoundedStaleness. Valid ranges, Single Region: 5 to 84600. Multi Region: 300 to 86400." + }, + "maxValue": 86400, + "minValue": 5 + }, + "serverVersion": { + "type": "string", + "defaultValue": "4.2", + "allowedValues": [ + "3.2", + "3.6", + "4.0", + "4.2" + ], + "metadata": { + "description": "Optional. Specifies the MongoDB server version to use." + } + }, + "sqlDatabases": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. SQL Databases configurations." + } + }, + "gremlinDatabases": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Gremlin Databases configurations." + } + }, + "mongodbDatabases": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. MongoDB Databases configurations." + } + }, + "capabilitiesToAdd": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of Cosmos DB capabilities for the account." + }, + "allowedValues": [ + "EnableCassandra", + "EnableTable", + "EnableGremlin", + "EnableMongo", + "DisableRateLimitingResponses", + "EnableServerless" + ] + }, + "backupPolicyType": { + "type": "string", + "defaultValue": "Periodic", + "metadata": { + "description": "Optional. Describes the mode of backups." + }, + "allowedValues": [ + "Periodic", + "Continuous" + ] + }, + "backupPolicyContinuousTier": { + "type": "string", + "defaultValue": "Continuous30Days", + "metadata": { + "description": "Optional. Configuration values for continuous mode backup." + }, + "allowedValues": [ + "Continuous30Days", + "Continuous7Days" + ] + }, + "backupIntervalInMinutes": { + "type": "int", + "defaultValue": 240, + "metadata": { + "description": "Optional. An integer representing the interval in minutes between two backups. Only applies to periodic backup type." + }, + "maxValue": 1440, + "minValue": 60 + }, + "backupRetentionIntervalInHours": { + "type": "int", + "defaultValue": 8, + "metadata": { + "description": "Optional. An integer representing the time (in hours) that each backup is retained. Only applies to periodic backup type." + }, + "maxValue": 720, + "minValue": 2 + }, + "backupStorageRedundancy": { + "type": "string", + "defaultValue": "Local", + "metadata": { + "description": "Optional. Enum to indicate type of backup residency. Only applies to periodic backup type." + }, + "allowedValues": [ + "Geo", + "Local", + "Zone" + ] + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of objects that describe RBAC permissions, format { roleDefinitionResourceId (string), principalId (string), principalType (enum), enabled (bool) }. Ref: https://docs.microsoft.com/en-us/azure/templates/microsoft.authorization/roleassignments?tabs=bicep" + } + }, + "diagnosticWorkspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace." + } + }, + "diagnosticStorageAccountId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account." + } + }, + "diagnosticEventHubAuthorizationRuleId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "diagnosticEventHubName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category." + } + }, + "diagnosticLogsRetentionInDays": { + "type": "int", + "defaultValue": 365, + "maxValue": 365, + "minValue": 0, + "metadata": { + "description": "Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely." + } + }, + "logsToEnable": { + "type": "array", + "defaultValue": [ + "DataPlaneRequests", + "MongoRequests", + "QueryRuntimeStatistics", + "PartitionKeyStatistics", + "PartitionKeyRUConsumption", + "ControlPlaneRequests", + "CassandraRequests", + "GremlinRequests", + "TableApiRequests" + ], + "allowedValues": [ + "DataPlaneRequests", + "MongoRequests", + "QueryRuntimeStatistics", + "PartitionKeyStatistics", + "PartitionKeyRUConsumption", + "ControlPlaneRequests", + "CassandraRequests", + "GremlinRequests", + "TableApiRequests" + ], + "metadata": { + "description": "Optional. The name of logs that will be streamed." + } + }, + "metricsToEnable": { + "type": "array", + "defaultValue": [ + "Requests" + ], + "allowedValues": [ + "Requests" + ], + "metadata": { + "description": "Optional. The name of metrics that will be streamed." + } + }, + "kvKeyUri": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Customer Managed Encryption Key." + } + }, + "crossTenant": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates if the module is used in a cross tenant scenario. If true, a resourceId must be provided in the role assignment's principal object." + } + }, + "privateLinkSettings": { + "type": "object", + "defaultValue": { + "subnetId": "1", + "vnetId": "1" + }, + "metadata": { + "description": "Settings Required to Enable Private Link" + } + }, + "keyVaultName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional: Key Vault Name to store secrets into" + } + }, + "databaseEndpointSecretName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional: To save storage account name into vault set the secret hame." + } + }, + "databasePrimaryKeySecretName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional: To save storage account key into vault set the secret hame." + } + }, + "databaseConnectionStringSecretName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional: To save storage account connectionstring into vault set the secret hame." + } + } + }, + "variables": { + "copy": [ + { + "name": "diagnosticsLogs", + "count": "[length(parameters('logsToEnable'))]", + "input": { + "category": "[parameters('logsToEnable')[copyIndex('diagnosticsLogs')]]", + "enabled": true, + "retentionPolicy": { + "enabled": true, + "days": "[parameters('diagnosticLogsRetentionInDays')]" + } + } + }, + { + "name": "diagnosticsMetrics", + "count": "[length(parameters('metricsToEnable'))]", + "input": { + "category": "[parameters('metricsToEnable')[copyIndex('diagnosticsMetrics')]]", + "timeGrain": null, + "enabled": true, + "retentionPolicy": { + "enabled": true, + "days": "[parameters('diagnosticLogsRetentionInDays')]" + } + } + }, + { + "name": "databaseAccount_locations", + "count": "[length(parameters('multiwriteRegions'))]", + "input": { + "failoverPriority": "[parameters('multiwriteRegions')[copyIndex('databaseAccount_locations')].failoverPriority]", + "isZoneRedundant": "[parameters('multiwriteRegions')[copyIndex('databaseAccount_locations')].isZoneRedundant]", + "locationName": "[parameters('multiwriteRegions')[copyIndex('databaseAccount_locations')].locationName]" + } + }, + { + "name": "capabilities", + "count": "[length(parameters('capabilitiesToAdd'))]", + "input": { + "name": "[parameters('capabilitiesToAdd')[copyIndex('capabilities')]]" + } + } + ], + "name": "[format('dba-{0}{1}', replace(parameters('resourceName'), '-', ''), uniqueString(resourceGroup().id, parameters('resourceName')))]", + "identityType": "[if(parameters('systemAssignedIdentity'), if(not(empty(parameters('userAssignedIdentities'))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(parameters('userAssignedIdentities'))), 'UserAssigned', 'None'))]", + "consistencyPolicy": { + "Eventual": { + "defaultConsistencyLevel": "Eventual" + }, + "ConsistentPrefix": { + "defaultConsistencyLevel": "ConsistentPrefix" + }, + "Session": { + "defaultConsistencyLevel": "Session" + }, + "BoundedStaleness": { + "defaultConsistencyLevel": "BoundedStaleness", + "maxStalenessPrefix": "[parameters('maxStalenessPrefix')]", + "maxIntervalInSeconds": "[parameters('maxIntervalInSeconds')]" + }, + "Strong": { + "defaultConsistencyLevel": "Strong" + } + }, + "kind": "[if(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('gremlinDatabases')))), 'GlobalDocumentDB', if(not(empty(parameters('mongodbDatabases'))), 'MongoDB', 'Parse'))]", + "enableReferencedModulesTelemetry": false, + "backupPolicy": "[if(equals(parameters('backupPolicyType'), 'Continuous'), createObject('type', parameters('backupPolicyType'), 'continuousModeProperties', createObject('tier', parameters('backupPolicyContinuousTier'))), createObject('type', parameters('backupPolicyType'), 'periodicModeProperties', createObject('backupIntervalInMinutes', parameters('backupIntervalInMinutes'), 'backupRetentionIntervalInHours', parameters('backupRetentionIntervalInHours'), 'backupStorageRedundancy', parameters('backupStorageRedundancy'))))]", + "databaseAccount_properties": "[union(createObject('databaseAccountOfferType', parameters('databaseAccountOfferType')), if(or(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('mongodbDatabases')))), not(empty(parameters('gremlinDatabases')))), createObject('consistencyPolicy', variables('consistencyPolicy')[parameters('defaultConsistencyLevel')], 'enableMultipleWriteLocations', if(empty(parameters('multiwriteRegions')), false(), true()), 'locations', if(empty(parameters('multiwriteRegions')), createArray(createObject('failoverPriority', 0, 'isZoneRedundant', false(), 'locationName', parameters('resourceLocation'))), variables('databaseAccount_locations')), 'capabilities', variables('capabilities'), 'backupPolicy', variables('backupPolicy')), createObject()), if(not(empty(parameters('sqlDatabases'))), createObject('enableAutomaticFailover', parameters('automaticFailover'), 'AnalyticalStorageConfiguration', createObject('schemaType', 'WellDefined'), 'defaultIdentity', if(not(empty(parameters('defaultIdentity'))), format('UserAssignedIdentity={0}', parameters('defaultIdentity')), 'FirstPartyIdentity'), 'enablePartitionKeyMonitor', true(), 'enablePartitionMerge', false(), 'keyVaultKeyUri', if(not(empty(parameters('kvKeyUri'))), parameters('kvKeyUri'), json('null'))), createObject()), if(not(empty(parameters('mongodbDatabases'))), createObject('apiProperties', createObject('serverVersion', parameters('serverVersion'))), createObject('EnabledApiTypes', createArray('Sql'))))]", + "enablePrivateLink": "[and(not(equals(parameters('privateLinkSettings').vnetId, '1')), not(equals(parameters('privateLinkSettings').subnetId, '1')))]", + "privateEndpointName": "[format('{0}-PrivateEndpoint', variables('name'))]", + "privateDNSZoneName": "privatelink.documents.azure.com" + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2022-08-15", + "name": "[if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name'))]", + "location": "[parameters('resourceLocation')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "[variables('identityType')]", + "userAssignedIdentities": "[if(not(empty(parameters('userAssignedIdentities'))), parameters('userAssignedIdentities'), createObject())]" + }, + "kind": "[variables('kind')]", + "properties": "[variables('databaseAccount_properties')]" + }, + { + "condition": "[parameters('enableDeleteLock')]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2017-04-01", + "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name')))]", + "name": "[format('{0}-lock', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name')))]", + "properties": { + "level": "CanNotDelete" + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name')))]" + ] + }, + { + "condition": "[or(or(or(not(empty(parameters('diagnosticStorageAccountId'))), not(empty(parameters('diagnosticWorkspaceId')))), not(empty(parameters('diagnosticEventHubAuthorizationRuleId')))), not(empty(parameters('diagnosticEventHubName'))))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name')))]", + "name": "storage-diagnostics", + "properties": { + "storageAccountId": "[if(not(empty(parameters('diagnosticStorageAccountId'))), parameters('diagnosticStorageAccountId'), null())]", + "workspaceId": "[if(not(empty(parameters('diagnosticWorkspaceId'))), parameters('diagnosticWorkspaceId'), null())]", + "eventHubAuthorizationRuleId": "[if(not(empty(parameters('diagnosticEventHubAuthorizationRuleId'))), parameters('diagnosticEventHubAuthorizationRuleId'), null())]", + "eventHubName": "[if(not(empty(parameters('diagnosticEventHubName'))), parameters('diagnosticEventHubName'), null())]", + "metrics": "[variables('diagnosticsMetrics')]", + "logs": "[variables('diagnosticsLogs')]", + "logAnalyticsDestinationType": "AzureDiagnostics" + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name')))]" + ] + }, + { + "condition": "[variables('enablePrivateLink')]", + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2021-02-01", + "name": "[variables('privateEndpointName')]", + "location": "[parameters('resourceLocation')]", + "properties": { + "subnet": { + "id": "[parameters('privateLinkSettings').subnetId]" + }, + "privateLinkServiceConnections": [ + { + "name": "[variables('privateEndpointName')]", + "properties": { + "privateLinkServiceId": "[resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name')))]", + "groupIds": [ + "Sql" + ] + } + } + ], + "customDnsConfigs": [ + { + "fqdn": "[variables('privateDNSZoneName')]" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name')))]" + ] + }, + { + "condition": "[variables('enablePrivateLink')]", + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}-link', variables('privateDNSZoneName'), variables('privateDNSZoneName'))]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[parameters('privateLinkSettings').vnetId]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', variables('privateDNSZoneName'))]" + ] + }, + { + "condition": "[variables('enablePrivateLink')]", + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[variables('privateDNSZoneName')]", + "location": "global" + }, + { + "condition": "[variables('enablePrivateLink')]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2022-01-01", + "name": "[format('{0}/dnsgroupname', variables('privateEndpointName'))]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "config1", + "properties": { + "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', variables('privateDNSZoneName'))]" + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', variables('privateDNSZoneName'))]", + "[resourceId('Microsoft.Network/privateEndpoints', variables('privateEndpointName'))]" + ] + }, + { + "copy": { + "name": "databaseAccount_sqlDatabases", + "count": "[length(parameters('sqlDatabases'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-{1}', deployment().name, parameters('sqlDatabases')[copyIndex()].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "databaseAccountName": { + "value": "[if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name'))]" + }, + "name": { + "value": "[parameters('sqlDatabases')[copyIndex()].name]" + }, + "throughput": { + "value": "[parameters('throughput')]" + }, + "maxThroughput": { + "value": "[parameters('maxThroughput')]" + }, + "containers": "[if(contains(parameters('sqlDatabases')[copyIndex()], 'containers'), createObject('value', parameters('sqlDatabases')[copyIndex()].containers), createObject('value', createArray()))]", + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.13.1.58284", + "templateHash": "10157814525819413114" + } + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the SQL database ." + } + }, + "containers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of containers to deploy in the SQL database." + } + }, + "maxThroughput": { + "type": "int", + "defaultValue": 400, + "metadata": { + "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." + } + }, + "throughput": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Request Units per second (for example 10000). Cannot be set together with `maxThroughput`." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the SQL database resource." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via the Customer Usage Attribution ID (GUID)." + } + } + }, + "variables": { + "enableReferencedModulesTelemetry": false + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', 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": [] + } + } + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "options": "[if(contains(reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), '2022-08-15').capabilities, createObject('name', 'EnableServerless')), createObject(), createObject('autoscaleSettings', if(equals(parameters('throughput'), -1), createObject('maxThroughput', parameters('maxThroughput')), null()), 'throughput', if(not(equals(parameters('throughput'), -1)), parameters('throughput'), null())))]", + "resource": { + "id": "[parameters('name')]" + } + } + }, + { + "copy": { + "name": "container", + "count": "[length(parameters('containers'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-{1}-sql-{2}', deployment().name, parameters('name'), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "databaseAccountName": { + "value": "[parameters('databaseAccountName')]" + }, + "sqlDatabaseName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('containers')[copyIndex()].name]" + }, + "paths": { + "value": "[parameters('containers')[copyIndex()].paths]" + }, + "kind": { + "value": "[parameters('containers')[copyIndex()].kind]" + }, + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.13.1.58284", + "templateHash": "6494983495030753329" + } + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "sqlDatabaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL Database. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the container." + } + }, + "throughput": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Request Units per second (for example 10000). Cannot be set together with `maxThroughput`." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the SQL Database resource." + } + }, + "paths": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of paths using which data within the container can be partitioned." + } + }, + "kind": { + "type": "string", + "defaultValue": "Hash", + "allowedValues": [ + "Hash", + "MultiHash", + "Range" + ], + "metadata": { + "description": "Optional. Indicates the kind of algorithm used for partitioning." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via the Customer Usage Attribution ID (GUID)." + } + } + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', 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": [] + } + } + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}/{2}', parameters('databaseAccountName'), parameters('sqlDatabaseName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": { + "id": "[parameters('name')]", + "partitionKey": { + "paths": "[parameters('paths')]", + "kind": "[parameters('kind')]" + } + }, + "options": "[if(or(contains(reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), '2022-08-15').capabilities, createObject('name', 'EnableServerless')), equals(parameters('throughput'), -1)), null(), createObject('throughput', parameters('throughput')))]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('name')]", + "metadata": { + "description": "The name of the container." + } + }, + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), parameters('sqlDatabaseName'), parameters('name'))]", + "metadata": { + "description": "The resource ID of the container." + } + }, + "resourceGroupName": { + "type": "string", + "value": "[resourceGroup().name]", + "metadata": { + "description": "The name of the resource group the container was created in." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), parameters('name'))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('name')]", + "metadata": { + "description": "The name of the SQL database." + } + }, + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), parameters('name'))]", + "metadata": { + "description": "The resource ID of the SQL database." + } + }, + "resourceGroupName": { + "type": "string", + "value": "[resourceGroup().name]", + "metadata": { + "description": "The name of the resource group the SQL database was created in." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name')))]" + ] + }, + { + "copy": { + "name": "databaseAccount_gremlinDatabases", + "count": "[length(parameters('gremlinDatabases'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-{1}', deployment().name, parameters('gremlinDatabases')[copyIndex()].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "databaseAccountName": { + "value": "[if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name'))]" + }, + "name": { + "value": "[parameters('gremlinDatabases')[copyIndex()].name]" + }, + "throughput": { + "value": "[parameters('throughput')]" + }, + "maxThroughput": { + "value": "[parameters('maxThroughput')]" + }, + "graphs": "[if(contains(parameters('gremlinDatabases')[copyIndex()], 'graphs'), createObject('value', parameters('gremlinDatabases')[copyIndex()].graphs), createObject('value', createArray()))]", + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.13.1.58284", + "templateHash": "2281214798785189196" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Gremlin database." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the Gremlin database resource." + } + }, + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Gremlin database. Required if the template is used in a standalone deployment." + } + }, + "graphs": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of graphs to deploy in the Gremlin database." + } + }, + "maxThroughput": { + "type": "int", + "defaultValue": 400, + "metadata": { + "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." + } + }, + "throughput": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Request Units per second (for example 10000). Cannot be set together with `maxThroughput`." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via the Customer Usage Attribution ID (GUID)." + } + } + }, + "variables": { + "enableReferencedModulesTelemetry": false + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', 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": [] + } + } + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "options": "[if(contains(reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), '2022-08-15').capabilities, createObject('name', 'EnableServerless')), createObject(), createObject('autoscaleSettings', if(equals(parameters('throughput'), -1), createObject('maxThroughput', parameters('maxThroughput')), null()), 'throughput', if(not(equals(parameters('throughput'), -1)), parameters('throughput'), null())))]", + "resource": { + "id": "[parameters('name')]" + } + } + }, + { + "copy": { + "name": "gremlinDatabase_gremlinGraphs", + "count": "[length(parameters('graphs'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-{1}-graph-{2}', deployment().name, parameters('name'), parameters('graphs')[copyIndex()].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('graphs')[copyIndex()].name]" + }, + "gremlinDatabaseName": { + "value": "[parameters('name')]" + }, + "databaseAccountName": { + "value": "[parameters('databaseAccountName')]" + }, + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "automaticIndexing": "[if(contains(parameters('graphs')[copyIndex()], 'automaticIndexing'), createObject('value', parameters('graphs')[copyIndex()].automaticIndexing), createObject('value', true()))]", + "partitionKeyPaths": "[if(not(empty(parameters('graphs')[copyIndex()].partitionKeyPaths)), createObject('value', parameters('graphs')[copyIndex()].partitionKeyPaths), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.13.1.58284", + "templateHash": "11007017926644196320" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the graph." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the Gremlin graph resource." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via the Customer Usage Attribution ID (GUID)." + } + }, + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "gremlinDatabaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Gremlin Database. Required if the template is used in a standalone deployment." + } + }, + "automaticIndexing": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates if the indexing policy is automatic." + } + }, + "partitionKeyPaths": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of paths using which data within the container can be partitioned." + } + } + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', 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": [] + } + } + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}/{2}', parameters('databaseAccountName'), parameters('gremlinDatabaseName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": { + "id": "[parameters('name')]", + "indexingPolicy": { + "automatic": "[parameters('automaticIndexing')]" + }, + "partitionKey": { + "paths": "[if(not(empty(parameters('partitionKeyPaths'))), parameters('partitionKeyPaths'), null())]" + } + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('name')]", + "metadata": { + "description": "The name of the graph." + } + }, + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs', parameters('databaseAccountName'), parameters('gremlinDatabaseName'), parameters('name'))]", + "metadata": { + "description": "The resource ID of the graph." + } + }, + "resourceGroupName": { + "type": "string", + "value": "[resourceGroup().name]", + "metadata": { + "description": "The name of the resource group the graph was created in." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/gremlinDatabases', parameters('databaseAccountName'), parameters('name'))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('name')]", + "metadata": { + "description": "The name of the Gremlin database." + } + }, + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/gremlinDatabases', parameters('databaseAccountName'), parameters('name'))]", + "metadata": { + "description": "The resource ID of the Gremlin database." + } + }, + "resourceGroupName": { + "type": "string", + "value": "[resourceGroup().name]", + "metadata": { + "description": "The name of the resource group the Gremlin database was created in." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name')))]" + ] + }, + { + "copy": { + "name": "databaseaccount_rbac", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-rbac-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), createObject('value', parameters('roleAssignments')[copyIndex()].description), createObject('value', ''))]", + "principals": { + "value": "[parameters('roleAssignments')[copyIndex()].principals]" + }, + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "principalType": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), createObject('value', parameters('roleAssignments')[copyIndex()].principalType), createObject('value', ''))]", + "resourceId": { + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name')))]" + }, + "crossTenant": { + "value": "[parameters('crossTenant')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.13.1.58284", + "templateHash": "8451379692843981129" + } + }, + "parameters": { + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "principals": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to. A resourceId is required when used in a cross tenant scenario (i.e. crossTenant is true)" + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource to apply the role assignment to." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"" + } + }, + "conditionVersion": { + "type": "string", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "crossTenant": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates if the module is used in a cross tenant scenario. If true, a resourceId must be provided in the role assignment's principal object." + } + } + }, + "variables": { + "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')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principals'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(resourceId('Microsoft.DocumentDB/databaseAccounts', last(split(parameters('resourceId'), '/'))), parameters('principals')[copyIndex()].id, parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principals')[copyIndex()].id]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(parameters('crossTenant'), parameters('principals')[copyIndex()].resourceId, null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name')))]" + ] + }, + { + "condition": "[and(not(empty(parameters('keyVaultName'))), not(empty(parameters('databaseEndpointSecretName'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-secret-name', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "name": { + "value": "[parameters('databaseEndpointSecretName')]" + }, + "value": { + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name'))), '2022-08-15').documentEndpoint]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.13.1.58284", + "templateHash": "16107441205299703534" + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret." + } + }, + "value": { + "type": "secureString", + "metadata": { + "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." + } + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "value": "[parameters('value')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('name')]", + "metadata": { + "description": "The name of the secret." + } + }, + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('name'))]", + "metadata": { + "description": "The resource ID of the secret." + } + }, + "resourceGroupName": { + "type": "string", + "value": "[resourceGroup().name]", + "metadata": { + "description": "The name of the resource group the secret was created in." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name')))]" + ] + }, + { + "condition": "[and(not(empty(parameters('keyVaultName'))), not(empty(parameters('databasePrimaryKeySecretName'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-secret-key', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "name": { + "value": "[parameters('databasePrimaryKeySecretName')]" + }, + "value": { + "value": "[listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name'))), '2022-08-15').primaryMasterKey]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.13.1.58284", + "templateHash": "16107441205299703534" + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret." + } + }, + "value": { + "type": "secureString", + "metadata": { + "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." + } + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "value": "[parameters('value')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('name')]", + "metadata": { + "description": "The name of the secret." + } + }, + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('name'))]", + "metadata": { + "description": "The resource ID of the secret." + } + }, + "resourceGroupName": { + "type": "string", + "value": "[resourceGroup().name]", + "metadata": { + "description": "The name of the resource group the secret was created in." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name')))]" + ] + }, + { + "condition": "[and(not(empty(parameters('keyVaultName'))), not(empty(parameters('databaseConnectionStringSecretName'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-secret-accountName', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "name": { + "value": "[parameters('databaseConnectionStringSecretName')]" + }, + "value": { + "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name'))), '2022-08-15').connectionStrings[0].connectionString]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.13.1.58284", + "templateHash": "16107441205299703534" + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret." + } + }, + "value": { + "type": "secureString", + "metadata": { + "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." + } + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "value": "[parameters('value')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('name')]", + "metadata": { + "description": "The name of the secret." + } + }, + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('name'))]", + "metadata": { + "description": "The resource ID of the secret." + } + }, + "resourceGroupName": { + "type": "string", + "value": "[resourceGroup().name]", + "metadata": { + "description": "The name of the resource group the secret was created in." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name')))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name'))]", + "metadata": { + "description": "The name of the database account." + } + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name')))]", + "metadata": { + "description": "The resource ID of the database account." + } + }, + "resourceGroupName": { + "type": "string", + "value": "[resourceGroup().name]", + "metadata": { + "description": "The name of the resource group the database account was created in." + } + }, + "systemAssignedPrincipalId": { + "type": "string", + "value": "[if(and(parameters('systemAssignedIdentity'), contains(reference(resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name'))), '2022-08-15', 'full').identity, 'principalId')), reference(resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name'))), '2022-08-15', 'full').identity.principalId, '')]", + "metadata": { + "description": "The principal ID of the system assigned identity." + } + }, + "location": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', if(greater(length(variables('name')), 44), substring(variables('name'), 0, 44), variables('name'))), '2022-08-15', 'full').location]", + "metadata": { + "description": "The location the resource was deployed into." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-azure-keyvault', variables('commonLayerConfig').name))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-log-analytics', variables('commonLayerConfig').name))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-user-managed-identity', variables('commonLayerConfig').name))]" + ] + }, + { + "condition": "[parameters('enablePrivateLink')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-cosmos-db-endpoint', variables('commonLayerConfig').name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "resourceName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-cosmos-db', variables('commonLayerConfig').name)), '2022-09-01').outputs.name.value]" + }, + "subnetResourceId": { + "value": "[format('{0}/subnets/{1}', createObject('new', if(equals(parameters('virtualNetworkNewOrExisting'), 'new'), reference(resourceId('Microsoft.Resources/deployments', format('{0}-virtual-network', variables('commonLayerConfig').name)), '2022-09-01').outputs.resourceId.value, null()), 'existing', resourceId(parameters('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', parameters('virtualNetworkName')))[parameters('virtualNetworkNewOrExisting')], parameters('aksSubnetName'))]" + }, + "serviceResourceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-cosmos-db', variables('commonLayerConfig').name)), '2022-09-01').outputs.id.value]" + }, + "groupIds": { + "value": [ + "sql" + ] + }, + "privateDnsZoneGroup": { + "value": { + "privateDNSResourceIds": [ + "[resourceId('Microsoft.Network/privateDnsZones', variables('cosmosDnsZoneName'))]" + ] + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.12.40.16777", + "templateHash": "18408830660327806831" + } + }, + "parameters": { + "resourceName": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "serviceResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the resource that needs to be connected to the network." + } + }, + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. Subtype(s) of the connection to be created. The allowed values depend on the type serviceResourceId refers to." + } + }, + "applicationSecurityGroups": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "type": "object", + "defaultValue": {}, + "metadata": { + "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." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "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'." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Tags." + } + }, + "lock": { + "type": "string", + "defaultValue": "NotSpecified", + "metadata": { + "description": "Optional. Specify the type of lock." + }, + "allowedValues": [ + "CanNotDelete", + "NotSpecified", + "ReadOnly" + ] + }, + "customDnsConfigs": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Manual PrivateLink Service Connections." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2022-05-01", + "name": "[parameters('resourceName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "applicationSecurityGroups": "[parameters('applicationSecurityGroups')]", + "customNetworkInterfaceName": "[parameters('customNetworkInterfaceName')]", + "ipConfigurations": "[parameters('ipConfigurations')]", + "manualPrivateLinkServiceConnections": "[parameters('manualPrivateLinkServiceConnections')]", + "customDnsConfigs": "[parameters('customDnsConfigs')]", + "privateLinkServiceConnections": [ + { + "name": "[parameters('resourceName')]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceId')]", + "groupIds": "[parameters('groupIds')]" + } + } + ], + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + { + "condition": "[not(equals(parameters('lock'), 'NotSpecified'))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2017-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('resourceName'))]", + "name": "[format('{0}-{1}-lock', parameters('resourceName'), parameters('lock'))]", + "properties": { + "level": "[parameters('lock')]", + "notes": "[if(equals(parameters('lock'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName'))]" + ] + }, + { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-{1}', deployment().name, parameters('resourceName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDNSResourceIds": { + "value": "[parameters('privateDnsZoneGroup').privateDNSResourceIds]" + }, + "privateEndpointName": { + "value": "[parameters('resourceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.12.40.16777", + "templateHash": "2962080553890533733" + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "maxLength": 5, + "minLength": 1, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + } + }, + "resourceName": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2022-05-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('resourceName'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "value": "[resourceGroup().name]", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + } + }, + "name": { + "type": "string", + "value": "[parameters('resourceName')]", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + } + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('resourceName'))]", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName'))]" + ] + }, + { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "[format('{0}-rbac-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": { + "value": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), parameters('roleAssignments')[copyIndex()].description, '')]" + }, + "principalIds": { + "value": "[parameters('roleAssignments')[copyIndex()].principalIds]" + }, + "principalType": { + "value": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), parameters('roleAssignments')[copyIndex()].principalType, '')]" + }, + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "condition": { + "value": "[if(contains(parameters('roleAssignments')[copyIndex()], 'condition'), parameters('roleAssignments')[copyIndex()].condition, '')]" + }, + "delegatedManagedIdentityResourceId": { + "value": "[if(contains(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), parameters('roleAssignments')[copyIndex()].delegatedManagedIdentityResourceId, '')]" + }, + "resourceId": { + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.12.40.16777", + "templateHash": "17398245239701469576" + } + }, + "parameters": { + "principalIds": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource to apply the role assignment to." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"" + } + }, + "conditionVersion": { + "type": "string", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Id of the delegated managed identity resource." + } + } + }, + "variables": { + "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')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principalIds'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', last(split(parameters('resourceId'), '/'))), parameters('principalIds')[copyIndex()], parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principalIds')[copyIndex()]]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName'))]" + ] + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "value": "[resourceGroup().name]", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + } + }, + "name": { + "type": "string", + "value": "[parameters('resourceName')]", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName'))]", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "location": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Network/privateEndpoints', parameters('resourceName')), '2022-05-01', 'full').location]", + "metadata": { + "description": "The location the resource was deployed into." + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', variables('cosmosDnsZoneName'))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-cosmos-db', variables('commonLayerConfig').name))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-virtual-network', variables('commonLayerConfig').name))]" + ] + }, + { + "condition": "[parameters('enableBastion')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-bastion', variables('manageLayerConfig').name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('bh-{0}{1}', replace(variables('manageLayerConfig').name, '-', ''), uniqueString(deployment().name, variables('manageLayerConfig').name))]" + }, + "vNetId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-virtual-network', variables('commonLayerConfig').name)), '2022-09-01').outputs.resourceId.value]" + }, + "location": { + "value": "[parameters('location')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "15023722152295838533" + }, + "name": "Bastion Hosts", + "description": "This module deploys a Bastion Host.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"" + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to 'AllLogs' to collect all logs." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Azure Bastion resource." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "vNetId": { + "type": "string", + "metadata": { + "description": "Required. Shared services Virtual Network resource identifier." + } + }, + "bastionSubnetPublicIpResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "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." + } + }, + "publicIPAddressObject": { + "type": "object", + "defaultValue": { + "name": "[format('{0}-pip', parameters('name'))]" + }, + "metadata": { + "description": "Optional. Specifies the properties of the Public IP to create and be used by Azure Bastion, if no existing public IP was provided." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Basic", + "allowedValues": [ + "Basic", + "Standard" + ], + "metadata": { + "description": "Optional. The SKU of this Bastion Host." + } + }, + "disableCopyPaste": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Copy Paste." + } + }, + "enableFileCopy": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Choose to disable or enable File Copy." + } + }, + "enableIpConnect": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable IP Connect." + } + }, + "enableKerberos": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Kerberos authentication." + } + }, + "enableShareableLink": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Shareable Link." + } + }, + "scaleUnits": { + "type": "int", + "defaultValue": 2, + "metadata": { + "description": "Optional. The scale units for the Bastion Host resource." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-bastionhost.{0}.{1}', replace('0.1.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "azureBastion": { + "type": "Microsoft.Network/bastionHosts", + "apiVersion": "2022-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('skuName')]" + }, + "properties": "[union(createObject('scaleUnits', if(equals(parameters('skuName'), 'Basic'), 2, parameters('scaleUnits')), 'ipConfigurations', createArray(createObject('name', 'IpConfAzureBastionSubnet', 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureBastionSubnet', parameters('vNetId')))), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('bastionSubnetPublicIpResourceId'))), parameters('bastionSubnetPublicIpResourceId'), reference('publicIPAddress').outputs.resourceId.value)))))), 'enableKerberos', parameters('enableKerberos')), if(equals(parameters('skuName'), 'Standard'), createObject('enableTunneling', equals(parameters('skuName'), 'Standard'), 'disableCopyPaste', parameters('disableCopyPaste'), 'enableFileCopy', parameters('enableFileCopy'), 'enableIpConnect', parameters('enableIpConnect'), 'enableShareableLink', parameters('enableShareableLink')), createObject()))]", + "dependsOn": [ + "publicIPAddress" + ] + }, + "azureBastion_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/bastionHosts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "azureBastion" + ] + }, + "azureBastion_diagnosticSettings": { + "copy": { + "name": "azureBastion_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/bastionHosts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "logs": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'AllLogs', 'enabled', true())))]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "azureBastion" + ] + }, + "azureBastion_roleAssignments": { + "copy": { + "name": "azureBastion_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/bastionHosts/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/bastionHosts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "azureBastion" + ] + }, + "publicIPAddress": { + "condition": "[empty(parameters('bastionSubnetPublicIpResourceId'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Bastion-PIP', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('publicIPAddressObject').name]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "lock": { + "value": "[parameters('lock')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'diagnosticSettings')]" + }, + "publicIPAddressVersion": "[if(contains(parameters('publicIPAddressObject'), 'publicIPAddressVersion'), createObject('value', parameters('publicIPAddressObject').publicIPAddressVersion), createObject('value', 'IPv4'))]", + "publicIPAllocationMethod": "[if(contains(parameters('publicIPAddressObject'), 'publicIPAllocationMethod'), createObject('value', parameters('publicIPAddressObject').publicIPAllocationMethod), createObject('value', 'Static'))]", + "publicIpPrefixResourceId": "[if(contains(parameters('publicIPAddressObject'), 'publicIPPrefixResourceId'), createObject('value', parameters('publicIPAddressObject').publicIPPrefixResourceId), createObject('value', ''))]", + "roleAssignments": "[if(contains(parameters('publicIPAddressObject'), 'roleAssignments'), createObject('value', parameters('publicIPAddressObject').roleAssignments), createObject('value', createArray()))]", + "skuName": "[if(contains(parameters('publicIPAddressObject'), 'skuName'), createObject('value', parameters('publicIPAddressObject').skuName), createObject('value', 'Standard'))]", + "skuTier": "[if(contains(parameters('publicIPAddressObject'), 'skuTier'), createObject('value', parameters('publicIPAddressObject').skuTier), createObject('value', 'Regional'))]", + "tags": { + "value": "[coalesce(tryGet(parameters('publicIPAddressObject'), 'tags'), parameters('tags'))]" + }, + "zones": "[if(contains(parameters('publicIPAddressObject'), 'zones'), createObject('value', parameters('publicIPAddressObject').zones), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "3488076626994379707" + }, + "name": "Public IP Addresses", + "description": "This module deploys a Public IP Address.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"" + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "dnsSettingsType": { + "type": "object", + "properties": { + "domainNameLabel": { + "type": "string", + "metadata": { + "description": "Required. The domain name label. The concatenation of the domain name label and the regionalized DNS zone make up the fully qualified domain name associated with the public IP address. If a domain name label is specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system." + } + }, + "domainNameLabelScope": { + "type": "string", + "allowedValues": [ + "", + "NoReuse", + "ResourceGroupReuse", + "SubscriptionReuse", + "TenantReuse" + ], + "metadata": { + "description": "Required. The domain name label scope. If a domain name label and a domain name label scope are specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system with a hashed value includes in FQDN." + } + }, + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Fully Qualified Domain Name of the A DNS record associated with the public IP. This is the concatenation of the domainNameLabel and the regionalized DNS zone." + } + }, + "reverseFqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The reverse FQDN. A user-visible, fully qualified domain name that resolves to this public IP address. If the reverseFqdn is specified, then a PTR DNS record is created pointing from the IP address in the in-addr.arpa domain to the reverse FQDN." + } + } + } + }, + "ddosSettingsType": { + "type": "object", + "properties": { + "ddosProtectionPlan": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "metadata": { + "description": "Required. The DDoS protection plan ID associated with the public IP address." + } + }, + "protectionMode": { + "type": "string", + "allowedValues": [ + "Enabled" + ], + "metadata": { + "description": "Required. The DDoS protection policy customizations." + } + } + } + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Public IP Address." + } + }, + "publicIpPrefixResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "publicIPAllocationMethod": { + "type": "string", + "defaultValue": "Static", + "allowedValues": [ + "Dynamic", + "Static" + ], + "metadata": { + "description": "Optional. The public IP address allocation method." + } + }, + "zones": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. A list of availability zones denoting the IP allocated for the resource needs to come from." + } + }, + "publicIPAddressVersion": { + "type": "string", + "defaultValue": "IPv4", + "allowedValues": [ + "IPv4", + "IPv6" + ], + "metadata": { + "description": "Optional. IP address version." + } + }, + "dnsSettings": { + "$ref": "#/definitions/dnsSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DNS settings of the public IP address." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Basic", + "Standard" + ], + "metadata": { + "description": "Optional. Name of a public IP address SKU." + } + }, + "skuTier": { + "type": "string", + "defaultValue": "Regional", + "allowedValues": [ + "Global", + "Regional" + ], + "metadata": { + "description": "Optional. Tier of a public IP address SKU." + } + }, + "ddosSettings": { + "$ref": "#/definitions/ddosSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan configuration associated with the public IP address." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "idleTimeoutInMinutes": { + "type": "int", + "defaultValue": 4, + "metadata": { + "description": "Optional. The idle timeout of the public IP address." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.2.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "publicIpAddress": { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('skuName')]", + "tier": "[parameters('skuTier')]" + }, + "zones": "[parameters('zones')]", + "properties": { + "ddosSettings": "[parameters('ddosSettings')]", + "dnsSettings": "[parameters('dnsSettings')]", + "publicIPAddressVersion": "[parameters('publicIPAddressVersion')]", + "publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]", + "publicIPPrefix": "[if(not(empty(parameters('publicIpPrefixResourceId'))), createObject('id', parameters('publicIpPrefixResourceId')), null())]", + "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]", + "ipTags": [] + } + }, + "publicIpAddress_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + }, + "publicIpAddress_roleAssignments": { + "copy": { + "name": "publicIpAddress_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + }, + "publicIpAddress_diagnosticSettings": { + "copy": { + "name": "publicIpAddress_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "metrics": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics', 'timeGrain', null(), 'enabled', true())))]", + "logs": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs', 'enabled', true())))]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the public IP address was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the public IP address." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the public IP address." + }, + "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('name'))]" + }, + "ipAddress": { + "type": "string", + "metadata": { + "description": "The public IP address of the public IP address resource." + }, + "value": "[if(contains(reference('publicIpAddress'), 'ipAddress'), reference('publicIpAddress').ipAddress, '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('publicIpAddress', '2023-04-01', 'full').location]" + } + } + } + } + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the Azure Bastion was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name the Azure Bastion." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID the Azure Bastion." + }, + "value": "[resourceId('Microsoft.Network/bastionHosts', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('azureBastion', '2022-11-01', 'full').location]" + }, + "ipConfAzureBastionSubnet": { + "type": "object", + "metadata": { + "description": "The Public IPconfiguration object for the AzureBastionSubnet." + }, + "value": "[reference('azureBastion').ipConfigurations[0]]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-virtual-network', variables('commonLayerConfig').name))]" + ] + }, + { + "condition": "[parameters('enableBastion')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "virtualMachine", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "vmName": { + "value": "[format('{0}-vm', variables('manageLayerConfig').name)]" + }, + "vmSize": { + "value": "[variables('manageLayerConfig').machine.vmSize]" + }, + "tags": { + "value": { + "layer": "[variables('manageLayerConfig').displayName]" + } + }, + "vmSubnetId": { + "value": "[format('{0}/subnets/{1}', createObject('new', if(equals(parameters('virtualNetworkNewOrExisting'), 'new'), reference(resourceId('Microsoft.Resources/deployments', format('{0}-virtual-network', variables('commonLayerConfig').name)), '2022-09-01').outputs.resourceId.value, null()), 'existing', resourceId(parameters('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', parameters('virtualNetworkName')))[parameters('virtualNetworkNewOrExisting')], parameters('vmSubnetName'))]" + }, + "vmAdminPasswordOrKey": "[if(empty(parameters('vmAdminPasswordOrKey')), createObject('reference', createObject('keyVault', createObject('id', resourceId('Microsoft.KeyVault/vaults', reference(resourceId('Microsoft.Resources/deployments', format('{0}-azure-keyvault', variables('commonLayerConfig').name)), '2022-09-01').outputs.name.value)), 'secretName', 'PrivateLinkSSHKey-public')), createObject('value', parameters('vmAdminPasswordOrKey')))]", + "vmAdminUsername": { + "value": "[parameters('vmAdminUsername')]" + }, + "workspaceName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-log-analytics', variables('commonLayerConfig').name)), '2022-09-01').outputs.name.value]" + }, + "authenticationType": "[if(empty(parameters('vmAdminPasswordOrKey')), createObject('value', 'sshPublicKey'), createObject('value', 'password'))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.24.24.22086", + "templateHash": "11705338384785440863" + } + }, + "parameters": { + "vmName": { + "type": "string", + "defaultValue": "simpleVM", + "metadata": { + "description": "Specifies the name of the virtual machine." + } + }, + "vmSize": { + "type": "string", + "defaultValue": "Standard_D2s_v3", + "metadata": { + "description": "Specifies the size of the virtual machine." + } + }, + "vmSubnetId": { + "type": "string", + "metadata": { + "description": "Specifies the resource id of the subnet hosting the virtual machine." + } + }, + "imagePublisher": { + "type": "string", + "defaultValue": "Canonical", + "metadata": { + "description": "Specifies the image publisher of the disk image used to create the virtual machine." + } + }, + "imageOffer": { + "type": "string", + "defaultValue": "0001-com-ubuntu-server-jammy", + "metadata": { + "description": "Specifies the offer of the platform image or marketplace image used to create the virtual machine." + } + }, + "imageSku": { + "type": "string", + "defaultValue": "22_04-lts-gen2", + "metadata": { + "description": "Specifies the Ubuntu version for the VM. This will pick a fully patched image of this given Ubuntu version." + } + }, + "authenticationType": { + "type": "string", + "defaultValue": "password", + "allowedValues": [ + "sshPublicKey", + "password" + ], + "metadata": { + "description": "Specifies the type of authentication when accessing the Virtual Machine. SSH key is recommended." + } + }, + "vmAdminUsername": { + "type": "string", + "metadata": { + "description": "Specifies the name of the administrator account of the virtual machine." + } + }, + "vmAdminPasswordOrKey": { + "type": "securestring", + "metadata": { + "description": "Specifies the SSH Key or password for the virtual machine. SSH key is recommended." + } + }, + "diskStorageAccountType": { + "type": "string", + "defaultValue": "Standard_LRS", + "allowedValues": [ + "Premium_LRS", + "StandardSSD_LRS", + "Standard_LRS", + "UltraSSD_LRS" + ], + "metadata": { + "description": "Specifies the storage account type for OS and data disk." + } + }, + "numDataDisks": { + "type": "int", + "defaultValue": 0, + "minValue": 0, + "maxValue": 64, + "metadata": { + "description": "Specifies the number of data disks of the virtual machine." + } + }, + "osDiskSize": { + "type": "int", + "defaultValue": 30, + "metadata": { + "description": "Specifies the size in GB of the OS disk of the VM." + } + }, + "dataDiskSize": { + "type": "int", + "defaultValue": 50, + "metadata": { + "description": "Specifies the size in GB of the OS disk of the virtual machine." + } + }, + "dataDiskCaching": { + "type": "string", + "defaultValue": "ReadWrite", + "metadata": { + "description": "Specifies the caching requirements for the data disks." + } + }, + "workspaceName": { + "type": "string", + "metadata": { + "description": "Specifies the name of the Log Analytics workspace." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Specifies the location." + } + }, + "tags": { + "type": "object", + "metadata": { + "description": "Specifies the resource tags." + } + } + }, + "variables": { + "vmNicName": "[format('{0}Nic', parameters('vmName'))]", + "linuxConfiguration": { + "disablePasswordAuthentication": true, + "ssh": { + "publicKeys": [ + { + "path": "[format('/home/{0}/.ssh/authorized_keys', parameters('vmAdminUsername'))]", + "keyData": "[parameters('vmAdminPasswordOrKey')]" + } + ] + }, + "provisionVMAgent": true + } + }, + "resources": [ + { + "type": "Microsoft.Network/networkInterfaces", + "apiVersion": "2021-08-01", + "name": "[variables('vmNicName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "subnet": { + "id": "[parameters('vmSubnetId')]" + } + } + } + ] + } + }, + { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2021-11-01", + "name": "[parameters('vmName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "osProfile": { + "computerName": "[parameters('vmName')]", + "adminUsername": "[parameters('vmAdminUsername')]", + "adminPassword": "[parameters('vmAdminPasswordOrKey')]", + "linuxConfiguration": "[if(equals(parameters('authenticationType'), 'password'), null(), variables('linuxConfiguration'))]" + }, + "storageProfile": { + "copy": [ + { + "name": "dataDisks", + "count": "[length(range(0, parameters('numDataDisks')))]", + "input": { + "caching": "[parameters('dataDiskCaching')]", + "diskSizeGB": "[parameters('dataDiskSize')]", + "lun": "[range(0, parameters('numDataDisks'))[copyIndex('dataDisks')]]", + "name": "[format('{0}-DataDisk{1}', parameters('vmName'), range(0, parameters('numDataDisks'))[copyIndex('dataDisks')])]", + "createOption": "Empty", + "managedDisk": { + "storageAccountType": "[parameters('diskStorageAccountType')]" + } + } + } + ], + "imageReference": { + "publisher": "[parameters('imagePublisher')]", + "offer": "[parameters('imageOffer')]", + "sku": "[parameters('imageSku')]", + "version": "latest" + }, + "osDisk": { + "name": "[format('{0}_OSDisk', parameters('vmName'))]", + "caching": "ReadWrite", + "createOption": "FromImage", + "diskSizeGB": "[parameters('osDiskSize')]", + "managedDisk": { + "storageAccountType": "[parameters('diskStorageAccountType')]" + } + } + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('vmNicName'))]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/networkInterfaces', variables('vmNicName'))]" + ] + }, + { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2023-03-01", + "name": "[format('{0}/{1}', parameters('vmName'), 'LogAnalytics')]", + "location": "[parameters('location')]", + "properties": { + "publisher": "Microsoft.EnterpriseCloud.Monitoring", + "type": "OmsAgentForLinux", + "typeHandlerVersion": "1.17", + "settings": { + "workspaceId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspaceName')), '2021-12-01-preview').customerId]", + "stopOnMultipleConnections": false + }, + "protectedSettings": { + "workspaceKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspaceName')), '2021-12-01-preview').primarySharedKey]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" + ] + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-bastion', variables('manageLayerConfig').name))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-azure-keyvault', variables('commonLayerConfig').name))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-log-analytics', variables('commonLayerConfig').name))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-virtual-network', variables('commonLayerConfig').name))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-azure-keyvault-sshkey', variables('commonLayerConfig').name))]" + ] } - ], - "outputs": { - "applicationClientId": { - "type": "string", - "value": "[parameters('applicationClientId')]" - } - } + ] } \ No newline at end of file diff --git a/bicep/main.bicep b/bicep/main.bicep index f156c35d..e3b10cec 100644 --- a/bicep/main.bicep +++ b/bicep/main.bicep @@ -12,14 +12,14 @@ param enableTelemetry bool = false @description('Feature Flag to Enable a Pod Subnet') param enablePodSubnet bool = false -// @description('Boolean indicating whether the VNet is new or existing') -// param virtualNetworkNewOrExisting string = 'new' +@description('Boolean indicating whether the VNet is new or existing') +param virtualNetworkNewOrExisting string = 'new' -// @description('Name of the Virtual Network (Optional: If exiting Network is selected)') -// param virtualNetworkName string = 'osdu-network' +@description('Name of the Virtual Network (Optional: If exiting Network is selected)') +param virtualNetworkName string = 'osdu-network' -// @description('Resource group of the VNet (Optional: If exiting Network is selected)') -// param virtualNetworkResourceGroup string = 'osdu-network' +@description('Resource group of the VNet (Optional: If exiting Network is selected)') +param virtualNetworkResourceGroup string = 'osdu-network' @description('VNet address prefix') param virtualNetworkAddressPrefix string = '10.1.0.0/16' @@ -68,6 +68,19 @@ param remoteVpnPrefix string = '' param remoteNetworkPrefix string = '192.168.1.0/24' +///////////////// +// Security Blade +///////////////// +@description('Feature Flag to Enable Private Link') +param enablePrivateLink bool = false + +@description('Optional. Customer Managed Encryption Key.') +param cmekConfiguration object = { + kvUrl: '' + keyName: '' + identityId: '' +} + ///////////////// // Settings Blade @@ -75,8 +88,6 @@ param remoteNetworkPrefix string = '192.168.1.0/24' @description('Specify the AD Application Client Id.') param applicationClientId string -output applicationClientId string = applicationClientId - ///////////////// // Bastion Blade @@ -85,6 +96,12 @@ output applicationClientId string = applicationClientId @description('Feature Flag to Enable Bastion') param enableBastion bool = false +@description('Specifies the name of the administrator account of the virtual machine.') +param vmAdminUsername string = enableBastion ? 'azureUser' : newGuid() + +@description('Specifies the SSH Key or password for the virtual machine. SSH key is recommended.') +@secure() +param vmAdminPasswordOrKey string = enableBastion ? '' : newGuid() //*****************************************************************// // Common Section // @@ -209,10 +226,10 @@ module logAnalytics 'br/public:avm/res/operational-insights/workspace:0.2.1' = { |__| \__| |_______| |__| \__/ \__/ \______/ | _| `._____||__|\__\ */ -// var vnetId = { -// new: virtualNetworkNewOrExisting == 'new' ? network.outputs.resourceId : null -// existing: resourceId(virtualNetworkResourceGroup, 'Microsoft.Network/virtualNetworks', virtualNetworkName) -// } +var vnetId = { + new: virtualNetworkNewOrExisting == 'new' ? network.outputs.resourceId : null + existing: resourceId(virtualNetworkResourceGroup, 'Microsoft.Network/virtualNetworks', virtualNetworkName) +} var nsgRules = { ssh_outbound: { @@ -619,3 +636,415 @@ module vpnGateway 'br/public:avm/res/network/vpn-gateway:0.1.0' = if (enableVpnG ] } } + + + + +/* + __ ___ ___________ ____ ____ ____ ___ __ __ __ .___________. +| |/ / | ____\ \ / / \ \ / / / \ | | | | | | | | +| ' / | |__ \ \/ / \ \/ / / ^ \ | | | | | | `---| |----` +| < | __| \_ _/ \ / / /_\ \ | | | | | | | | +| . \ | |____ | | \ / / _____ \ | `--' | | `----. | | +|__|\__\ |_______| |__| \__/ /__/ \__\ \______/ |_______| |__| +*/ + +var vaultDNSZoneName = 'privatelink.vaultcore.azure.net' + +module keyvault 'br:osdubicep.azurecr.io/public/azure-keyvault:1.0.7' = { + name: '${commonLayerConfig.name}-azure-keyvault' + params: { + resourceName: commonLayerConfig.name + location: location + + // Assign Tags + tags: { + layer: commonLayerConfig.displayName + } + + // Hook up Diagnostics + diagnosticWorkspaceId: logAnalytics.outputs.resourceId + diagnosticLogsRetentionInDays: 0 + + // Configure Access + accessPolicies: [ + { + principalId: stampIdentity.outputs.principalId + permissions: { + secrets: [ + 'get' + 'list' + 'set' + ] + certificates: [ + 'create' + 'get' + 'list' + ] + } + } + ] + + // Configure Secrets + secretsObject: { secrets: [ + // Misc Secrets + { + secretName: commonLayerConfig.secrets.tenantId + secretValue: subscription().tenantId + } + { + secretName: commonLayerConfig.secrets.subscriptionId + secretValue: subscription().subscriptionId + } + // Azure AD Secrets + { + secretName: commonLayerConfig.secrets.clientId + secretValue: applicationClientId + } + { + secretName: commonLayerConfig.secrets.applicationPrincipalId + secretValue: applicationClientId + } + // Managed Identity + { + secretName: commonLayerConfig.secrets.stampIdentity + secretValue: stampIdentity.outputs.principalId + } + ]} + + // Assign RBAC + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principals: [ + { + id: stampIdentity.outputs.principalId + resourceId: stampIdentity.outputs.resourceId + } + ] + principalType: 'ServicePrincipal' + } + ] + } +} + +module keyvaultSecrets './modules/keyvault_secrets.bicep' = { + name: '${commonLayerConfig.name}-log-analytics-secrets' + params: { + // Persist Secrets to Vault + keyVaultName: keyvault.outputs.name + workspaceName: logAnalytics.outputs.name + workspaceIdName: commonLayerConfig.secrets.logAnalyticsId + workspaceKeySecretName: commonLayerConfig.secrets.logAnalyticsKey + } +} + +module sshKey 'br:osdubicep.azurecr.io/public/script-sshkeypair:1.0.3' = if (enableBastion) { + name: '${commonLayerConfig.name}-azure-keyvault-sshkey' + params: { + kvName: keyvault.outputs.name + location: location + + useExistingManagedIdentity: true + managedIdentityName: stampIdentity.outputs.name + existingManagedIdentitySubId: subscription().subscriptionId + existingManagedIdentityResourceGroupName:resourceGroup().name + + sshKeyName: 'PrivateLinkSSHKey-' + + cleanupPreference: 'Always' + } +} + +module certificates './modules/script-kv-certificate/main.bicep' = { + name: '${commonLayerConfig.name}-azure-keyvault-cert' + params: { + kvName: keyvault.outputs.name + location: location + + useExistingManagedIdentity: true + managedIdentityName: stampIdentity.outputs.name + existingManagedIdentitySubId: subscription().subscriptionId + existingManagedIdentityResourceGroupName: resourceGroup().name + + certificateNames: [ + 'https-certificate' + ] + initialScriptDelay: '0' + validity: 24 + } +} + +resource vaultDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = if (enablePrivateLink) { + name: vaultDNSZoneName + location: 'global' + properties: {} +} + +module vaultEndpoint 'br:osdubicep.azurecr.io/public/private-endpoint:1.0.1' = if (enablePrivateLink) { + name: '${commonLayerConfig.name}-azure-keyvault-endpoint' + params: { + resourceName: keyvault.outputs.name + subnetResourceId: '${vnetId[virtualNetworkNewOrExisting]}/subnets/${aksSubnetName}' + + groupIds: [ 'vault'] + privateDnsZoneGroup: { + privateDNSResourceIds: [vaultDNSZone.id] + } + serviceResourceId: keyvault.outputs.id + } + dependsOn: [ + network + vaultDNSZone + ] +} + +resource existingVault 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: keyvault.outputs.name +} + + +/* _______.___________. ______ .______ ___ _______ _______ + / | | / __ \ | _ \ / \ / _____|| ____| + | (----`---| |----`| | | | | |_) | / ^ \ | | __ | |__ + \ \ | | | | | | | / / /_\ \ | | |_ | | __| +.----) | | | | `--' | | |\ \----./ _____ \ | |__| | | |____ +|_______/ |__| \______/ | _| `._____/__/ \__\ \______| |_______| +*/ + +var storageDNSZoneForwarder = 'blob.${environment().suffixes.storage}' +var storageDnsZoneName = 'privatelink.${storageDNSZoneForwarder}' + +module configStorage 'br:osdubicep.azurecr.io/public/storage-account:1.0.7' = { + name: '${commonLayerConfig.name}-azure-storage' + params: { + resourceName: commonLayerConfig.name + location: location + + // Assign Tags + tags: { + layer: commonLayerConfig.displayName + } + + // Hook up Diagnostics + diagnosticWorkspaceId: logAnalytics.outputs.resourceId + diagnosticLogsRetentionInDays: 0 + + // Configure Service + sku: commonLayerConfig.storage.sku + tables: commonLayerConfig.storage.tables + + // Assign RBAC + roleAssignments: [ + { + roleDefinitionIdOrName: 'Contributor' + principals: [ + { + id: stampIdentity.outputs.principalId + resourceId: stampIdentity.outputs.resourceId + } + ] + principalType: 'ServicePrincipal' + } + ] + + // Hookup Customer Managed Encryption Key + cmekConfiguration: cmekConfiguration + + // Persist Secrets to Vault + keyVaultName: keyvault.outputs.name + storageAccountSecretName: commonLayerConfig.secrets.storageAccountName + storageAccountKeySecretName: commonLayerConfig.secrets.storageAccountKey + } +} + +resource storageDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = if (enablePrivateLink) { + name: storageDnsZoneName + location: 'global' + properties: {} +} + +module storageEndpoint 'br:osdubicep.azurecr.io/public/private-endpoint:1.0.1' = if (enablePrivateLink) { + name: '${commonLayerConfig.name}-azure-storage-endpoint' + params: { + resourceName: configStorage.outputs.name + subnetResourceId: '${vnetId[virtualNetworkNewOrExisting]}/subnets/${aksSubnetName}' + serviceResourceId: configStorage.outputs.id + groupIds: [ 'blob'] + privateDnsZoneGroup: { + privateDNSResourceIds: [storageDNSZone.id] + } + } + dependsOn: [ + network + storageDNSZone + ] +} + + +/* + _______ .______ ___ .______ __ __ + / _____|| _ \ / \ | _ \ | | | | +| | __ | |_) | / ^ \ | |_) | | |__| | +| | |_ | | / / /_\ \ | ___/ | __ | +| |__| | | |\ \----./ _____ \ | | | | | | + \______| | _| `._____/__/ \__\ | _| |__| |__| +*/ + +var cosmosDnsZoneName = 'privatelink.documents.azure.com' + +module database 'br:osdubicep.azurecr.io/public/cosmos-db:1.0.17' = { + name: '${commonLayerConfig.name}-cosmos-db' + params: { + resourceName: commonLayerConfig.name + resourceLocation: location + + // Assign Tags + tags: { + layer: commonLayerConfig.displayName + } + + // Hook up Diagnostics + diagnosticWorkspaceId: logAnalytics.outputs.resourceId + diagnosticLogsRetentionInDays: 0 + + // Configure Service + capabilitiesToAdd: [ + 'EnableGremlin' + ] + gremlinDatabases: [ + { + name: commonLayerConfig.database.name + graphs: commonLayerConfig.database.graphs + } + ] + throughput: commonLayerConfig.database.throughput + backupPolicyType: commonLayerConfig.database.backup + + // Assign RBAC + roleAssignments: [ + { + roleDefinitionIdOrName: 'Contributor' + principals: [ + { + id: stampIdentity.outputs.principalId + resourceId: stampIdentity.outputs.resourceId + } + ] + principalType: 'ServicePrincipal' + } + ] + + // Hookup Customer Managed Encryption Key + systemAssignedIdentity: false + userAssignedIdentities: !empty(cmekConfiguration.identityId) ? { + '${stampIdentity.outputs.resourceId}': {} + '${cmekConfiguration.identityId}': {} + } : { + '${stampIdentity.outputs.resourceId}': {} + } + defaultIdentity: !empty(cmekConfiguration.identityId) ? cmekConfiguration.identityId : '' + kvKeyUri: !empty(cmekConfiguration.kvUrl) && !empty(cmekConfiguration.keyName) ? '${cmekConfiguration.kvUrl}/keys/${cmekConfiguration.keyName}' : '' + + // Persist Secrets to Vault + keyVaultName: keyvault.outputs.name + databaseEndpointSecretName: commonLayerConfig.secrets.cosmosEndpoint + databasePrimaryKeySecretName: commonLayerConfig.secrets.cosmosPrimaryKey + databaseConnectionStringSecretName: commonLayerConfig.secrets.cosmosConnectionString + } +} + +resource cosmosDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = if (enablePrivateLink) { + name: cosmosDnsZoneName + location: 'global' + properties: {} +} +module graphEndpoint 'br:osdubicep.azurecr.io/public/private-endpoint:1.0.1' = if (enablePrivateLink) { + name: '${commonLayerConfig.name}-cosmos-db-endpoint' + params: { + resourceName: database.outputs.name + subnetResourceId: '${vnetId[virtualNetworkNewOrExisting]}/subnets/${aksSubnetName}' + serviceResourceId: database.outputs.id + groupIds: [ 'sql'] + privateDnsZoneGroup: { + privateDNSResourceIds: [cosmosDNSZone.id] + } + } + dependsOn: [ + network + cosmosDNSZone + ] +} + + + +//*****************************************************************// +// Manage Section // +//*****************************************************************// + +///////////////////////////////// +// Configuration +///////////////////////////////// +var manageLayerConfig = { + name: 'manage' + displayName: 'Manage Resources' + machine: { + vmSize: 'Standard_DS3_v2' + imagePublisher: 'Canonical' + imageOffer: 'UbuntuServer' + imageSku: '18.04-LTS' + authenticationType: 'password' + } +} + +/*.______ ___ _______.___________. __ ______ .__ __. +| _ \ / \ / | || | / __ \ | \ | | +| |_) | / ^ \ | (----`---| |----`| | | | | | | \| | +| _ < / /_\ \ \ \ | | | | | | | | | . ` | +| |_) | / _____ \ .----) | | | | | | `--' | | |\ | +|______/ /__/ \__\ |_______/ |__| |__| \______/ |__| \__| +*/ + +module bastionHost 'br/public:avm/res/network/bastion-host:0.1.0' = if (enableBastion) { + name: '${manageLayerConfig.name}-bastion' + params: { + name: 'bh-${replace(manageLayerConfig.name, '-', '')}${uniqueString(deployment().name, manageLayerConfig.name)}' + vNetId: network.outputs.resourceId + location: location + enableTelemetry: enableTelemetry + } +} + +/* +.___ ___. ___ ______ __ __ __ .__ __. _______ +| \/ | / \ / || | | | | | | \ | | | ____| +| \ / | / ^ \ | ,----'| |__| | | | | \| | | |__ +| |\/| | / /_\ \ | | | __ | | | | . ` | | __| +| | | | / _____ \ | `----.| | | | | | | |\ | | |____ +|__| |__| /__/ \__\ \______||__| |__| |__| |__| \__| |_______| + +*/ + +module virtualMachine './modules/virtual_machine.bicep' = if (enableBastion) { + name: 'virtualMachine' + params: { + vmName: '${manageLayerConfig.name}-vm' + vmSize: manageLayerConfig.machine.vmSize + + // Assign Tags + tags: { + layer: manageLayerConfig.displayName + } + + vmSubnetId: '${vnetId[virtualNetworkNewOrExisting]}/subnets/${vmSubnetName}' + vmAdminPasswordOrKey: empty(vmAdminPasswordOrKey) ? existingVault.getSecret('PrivateLinkSSHKey-public') : vmAdminPasswordOrKey + vmAdminUsername: vmAdminUsername + workspaceName: logAnalytics.outputs.name + authenticationType: empty(vmAdminPasswordOrKey) ? 'sshPublicKey' : 'password' + } + dependsOn: [ + logAnalytics + bastionHost + sshKey + ] +} diff --git a/bicep/main.parameters.json b/bicep/main.parameters.json index 335d8a9f..40b7c0bd 100644 --- a/bicep/main.parameters.json +++ b/bicep/main.parameters.json @@ -13,6 +13,15 @@ }, "enableBastion": { "value": "${ENABLE_BASTION}" + }, + "vpnSharedKey": { + "value": "${VPN_SHARED_KEY}" + }, + "remoteVpnPrefix": { + "value": "${REMOTE_VPN_PREFIX}" + }, + "remoteNetworkPrefix": { + "value": "${REMOTE_NETWORK_PREFIX}" } } } \ No newline at end of file diff --git a/bicep/modules/keyvault_secrets.bicep b/bicep/modules/keyvault_secrets.bicep new file mode 100644 index 00000000..f416576f --- /dev/null +++ b/bicep/modules/keyvault_secrets.bicep @@ -0,0 +1,38 @@ +@description('Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment.') +param keyVaultName string + +@description('Conditional. The name of the Analytics Workspace. Required if the template is used in a standalone deployment.') +param workspaceName string + +@description('Required. The name of the secret.') +param workspaceIdName string + +@description('Required. The name of the secret.') +param workspaceKeySecretName string + +resource logAnaltyics 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = { + name: workspaceName +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +resource keySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: workspaceKeySecretName + parent: keyVault + + properties: { + value: logAnaltyics.listKeys().primarySharedKey + } +} + +resource idSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: workspaceIdName + parent: keyVault + + properties: { + value: logAnaltyics.id + } +} + diff --git a/bicep/modules/script-kv-certificate/README.md b/bicep/modules/script-kv-certificate/README.md new file mode 100644 index 00000000..ee6f6268 --- /dev/null +++ b/bicep/modules/script-kv-certificate/README.md @@ -0,0 +1,147 @@ +# Key Vault Certificate + +This module creates a Key Vault Certificate and stores it in an Azure Key Vault + +## Details + +{{Add detailed information about the module}} + +## Parameters + +| Name | Type | Required | Description | +| :----------------------------------------- | :------------: | :------: | :------------------------------------------------------------------------------------------------------------ | +| `kvName` | `string` | Yes | The name of the Azure Key Vault | +| `location` | `string` | Yes | The location to deploy the resources to | +| `forceUpdateTag` | `string` | No | How the deployment script should be forced to execute | +| `rbacRolesNeededOnKV` | `string` | No | The RoleDefinitionId required for the DeploymentScript resource to interact with KeyVault | +| `useExistingManagedIdentity` | `bool` | No | Does the Managed Identity already exists, or should be created | +| `managedIdentityName` | `string` | No | Name of the Managed Identity resource | +| `existingManagedIdentitySubId` | `string` | No | For an existing Managed Identity, the Subscription Id it is located in | +| `existingManagedIdentityResourceGroupName` | `string` | No | For an existing Managed Identity, the Resource Group it is located in | +| `certificateNames` | `array` | Yes | The names of the certificate to create. Use when creating many certificates. | +| `certificateCommonNames` | `array` | No | The common names of the certificate to create. Use when creating many certificates. | +| `initialScriptDelay` | `string` | No | A delay before the script import operation starts. Primarily to allow Azure AAD Role Assignments to propagate | +| `cleanupPreference` | `string` | No | When the script resource is cleaned up | +| `issuerName` | `string` | No | Self, or user defined {IssuerName} for certificate signing | +| `issuerProvider` | `string` | No | Certificate Issuer Provider, DigiCert, GlobalSign, or internal options may be used. | +| `disabled` | `bool` | No | Create certificate in disabled state. Default: false | +| `accountId` | `string` | No | Account ID of Certificate Issuer Account | +| `issuerPassword` | `securestring` | No | Password of Certificate Issuer Account | +| `organizationId` | `string` | No | Organization ID of Certificate Issuer Account | +| `isCrossTenant` | `bool` | No | Override this parameter if using this in cross tenant scenarios | +| `reuseKey` | `bool` | No | The default policy might cause errors about CSR being used before, so set this to false if that happens | +| `validity` | `int` | No | Optional. Override default validityInMonths 12 value | +| `performRoleAssignment` | `bool` | No | Set to false to disable role assignments within this module. Default: true | + +## Outputs + +| Name | Type | Description | +| :-------------------------------- | :-----: | :------------------------------------------------- | +| `certificateNames` | `array` | Certificate names | +| `certificateSecretIds` | `array` | KeyVault secret ids to the created version | +| `certificateSecretIdUnversioneds` | `array` | KeyVault secret ids which uses the unversioned uri | +| `certificateThumbpints` | `array` | Certificate Thumbprints | +| `certificateThumbprintHexs` | `array` | Certificate Thumbprints (in hex) | + +## Examples + +### Single KeyVault Certificate + +Creates a single self-signed certificate in Azure KeyVault. + +```bicep +param location string = resourceGroup().location +param akvName string = 'yourAzureKeyVault' +param certificateName string = 'myapp' + +module kvCert 'br/public:deployment-scripts/create-kv-certificate:3.4' = { + name: 'akvCertSingle' + params: { + akvName: akvName + location: location + certificateName: certificateName + } +} +output SecretId string = akvCertSingle.outputs.certificateSecretId +output Thumbprint string = akvCertSingle.outputs.certificateThumbprintHex + +``` + +### Single KeyVault Certificate with fqdn common name + +Creates a single self-signed certificate in Azure KeyVault using a specific certificate common name. + +```bicep +param location string = resourceGroup().location +param akvName string = 'yourAzureKeyVault' +param certificateName string = 'myapp' +param certificateCommonName string = '${certificateName}.mydomain.local' + +module kvCert 'br/public:deployment-scripts/create-kv-certificate:3.4' = { + name: 'akvCertSingle' + params: { + akvName: akvName + location: location + certificateNames: [certificateName] + certificateCommonNames: [certificateCommonName] + } +} +output SecretId string = akvCertSingle.outputs.certificateSecretIds[0] +output Thumbprint string = akvCertSingle.outputs.certificateThumbprintHexs[0] + +``` + +### Multiple KeyVault Certificates + +Create multiple self-signed certificates in Azure KeyVault + +```bicep +param location string = resourceGroup().location +param akvName string = 'yourAzureKeyVault' +param certificateNames array = [ + 'myapp' + 'myotherapp' +] + +module kvCert 'br/public:deployment-scripts/create-kv-certificate:3.1.1' = { + name: 'akvCert-${certificateName}' + params: { + akvName: akvName + location: location + certificateNames: certificateNames + } +} + +@description('Array of info from each Certificate') +output createdCertificates array = [for (certificateName, i) in certificateNames: { + certificateName: kvCert.outputs.certificateNames[i] + certificateSecretId: kvCert.outputs.certificateSecretIds[i] + certificateThumbprint: kvCert.outputs.certificateThumbprintHexs[i] +}] +``` + +### Create Signed Certificate + +Using `DigiCert` or `GlobalSign` first requires account setup described [here](https://learn.microsoft.com/en-us/azure/key-vault/certificates/how-to-integrate-certificate-authority) + +```bicep +param accountId +@secure +param issuerPassword +param organizationId + +module signedCert 'br/public:deployment-scripts/create-kv-certificate:3.1.1' = { + name: 'akvCert-${certificateName}' + params: { + akvName: akvName + location: location + certificateName: [certificateName] + certificateCommonName: ['customdomain.com'] + issuerName: 'MyCert' + issuerProvider: 'DigiCert' + accountId: accountId + issuerPassword: issuerPassword + organizationId: organizationId + } +}] +``` \ No newline at end of file diff --git a/bicep/modules/script-kv-certificate/main.bicep b/bicep/modules/script-kv-certificate/main.bicep new file mode 100644 index 00000000..2cd2c3fd --- /dev/null +++ b/bicep/modules/script-kv-certificate/main.bicep @@ -0,0 +1,170 @@ +metadata name = 'Key Vault Certificate' +metadata description = 'This module creates a Key Vault Certificate and stores it in an Azure Key Vault' +metadata owner = 'azure-global-energy' + +@description('The name of the Azure Key Vault') +param kvName string + +@description('The location to deploy the resources to') +param location string + +@description('How the deployment script should be forced to execute') +param forceUpdateTag string = utcNow() + +@description('The RoleDefinitionId required for the DeploymentScript resource to interact with KeyVault') +param rbacRolesNeededOnKV string = 'a4417e6f-fecd-4de8-b567-7b0420556985' //KeyVault Certificate Officer + +@description('Does the Managed Identity already exists, or should be created') +param useExistingManagedIdentity bool = false + +@description('Name of the Managed Identity resource') +param managedIdentityName string = 'id-KeyVaultCertificateCreator-${location}' + +@description('For an existing Managed Identity, the Subscription Id it is located in') +param existingManagedIdentitySubId string = subscription().subscriptionId + +@description('For an existing Managed Identity, the Resource Group it is located in') +param existingManagedIdentityResourceGroupName string = resourceGroup().name + +@description('The names of the certificate to create. Use when creating many certificates.') +param certificateNames array + +@description('The common names of the certificate to create. Use when creating many certificates.') +param certificateCommonNames array = certificateNames + +@description('A delay before the script import operation starts. Primarily to allow Azure AAD Role Assignments to propagate') +param initialScriptDelay string = '0' + +@allowed([ + 'OnSuccess' + 'OnExpiration' + 'Always' +]) +@description('When the script resource is cleaned up') +param cleanupPreference string = 'OnSuccess' + +@description('Self, or user defined {IssuerName} for certificate signing') +param issuerName string = 'Self' + +@description('Certificate Issuer Provider, DigiCert, GlobalSign, or internal options may be used.') +param issuerProvider string = '' + +@description('Create certificate in disabled state. Default: false') +param disabled bool = false + +@description('Account ID of Certificate Issuer Account') +param accountId string = '' + +@description('Password of Certificate Issuer Account') +@secure() +param issuerPassword string = '' + +@description('Organization ID of Certificate Issuer Account') +param organizationId string = '' + +@description('Override this parameter if using this in cross tenant scenarios') +param isCrossTenant bool = false + +@description('The default policy might cause errors about CSR being used before, so set this to false if that happens') +param reuseKey bool = true + +@minValue(1) +@maxValue(1200) +@description('Optional. Override default validityInMonths 12 value') +param validity int = 12 + +@description('Set to false to disable role assignments within this module. Default: true') +param performRoleAssignment bool = true + +resource akv 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: kvName +} + +@description('A new managed identity that will be created in this Resource Group, this is the default option') +resource newDepScriptId 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = if (!useExistingManagedIdentity) { + name: managedIdentityName + location: location +} + +@description('An existing managed identity that could exist in another sub/rg') +resource existingDepScriptId 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' existing = if (useExistingManagedIdentity) { + name: managedIdentityName + scope: resourceGroup(existingManagedIdentitySubId, existingManagedIdentityResourceGroupName) +} + +var delegatedManagedIdentityResourceId = useExistingManagedIdentity ? existingDepScriptId.id : newDepScriptId.id + +resource rbacKv 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (performRoleAssignment) { + name: guid(akv.id, rbacRolesNeededOnKV, managedIdentityName, string(useExistingManagedIdentity)) + scope: akv + properties: { + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', rbacRolesNeededOnKV) + principalId: useExistingManagedIdentity ? existingDepScriptId.properties.principalId : newDepScriptId.properties.principalId + principalType: 'ServicePrincipal' + delegatedManagedIdentityResourceId: isCrossTenant ? delegatedManagedIdentityResourceId : null + } +} + +resource createImportCerts 'Microsoft.Resources/deploymentScripts@2020-10-01' = [for (certificateName, index) in certificateNames: { + name: 'AKV-Cert-${akv.name}-${replace(replace(certificateName, ':', ''), '/', '-')}' + location: location + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${useExistingManagedIdentity ? existingDepScriptId.id : newDepScriptId.id}': {} + } + } + kind: 'AzureCLI' + dependsOn: [ + rbacKv + ] + properties: { + forceUpdateTag: forceUpdateTag + azCliVersion: '2.35.0' + timeout: 'PT10M' + retentionInterval: 'P1D' + environmentVariables: [ + { name: 'akvName', value: kvName } + { name: 'certName', value: certificateName } + { name: 'certCommonName', value: certificateCommonNames[index] } + { name: 'initialDelay', value: initialScriptDelay } + { name: 'issuerName', value: issuerName } + { name: 'issuerProvider', value: issuerProvider } + { name: 'disabled', value: toLower(string(disabled)) } + { name: 'retryMax', value: '10' } + { name: 'retrySleep', value: '5s' } + { name: 'accountId', value: accountId } + { name: 'issuerPassword', secureValue: issuerPassword } + { name: 'organizationId', value: organizationId } + { name: 'reuseKey', value: toLower(string(reuseKey)) } + { name: 'validity', value: string(validity) } + ] + scriptContent: loadTextContent('script.sh') + cleanupPreference: cleanupPreference + } +}] + +@description('Certificate names') +output certificateNames array = [for (certificateName, index) in certificateNames: [ + createImportCerts[index].properties.outputs.name +]] + +@description('KeyVault secret ids to the created version') +output certificateSecretIds array = [for (certificateName, index) in certificateNames: [ + createImportCerts[index].properties.outputs.certSecretId.versioned +]] + +@description('KeyVault secret ids which uses the unversioned uri') +output certificateSecretIdUnversioneds array = [for (certificateName, index) in certificateNames: [ + createImportCerts[index].properties.outputs.certSecretId.unversioned +]] + +@description('Certificate Thumbprints') +output certificateThumbpints array = [for (certificateName, index) in certificateNames: [ + createImportCerts[index].properties.outputs.thumbprint +]] + +@description('Certificate Thumbprints (in hex)') +output certificateThumbprintHexs array = [for (certificateName, index) in certificateNames: [ + createImportCerts[index].properties.outputs.thumbprintHex +]] diff --git a/bicep/modules/script-kv-certificate/main.json b/bicep/modules/script-kv-certificate/main.json new file mode 100644 index 00000000..9e89dc20 --- /dev/null +++ b/bicep/modules/script-kv-certificate/main.json @@ -0,0 +1,345 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "11944081156064634174" + }, + "name": "Key Vault Certificate", + "description": "This module creates a Key Vault Certificate and stores it in an Azure Key Vault", + "owner": "azure-global-energy" + }, + "parameters": { + "kvName": { + "type": "string", + "metadata": { + "description": "The name of the Azure Key Vault" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "The location to deploy the resources to" + } + }, + "forceUpdateTag": { + "type": "string", + "defaultValue": "[utcNow()]", + "metadata": { + "description": "How the deployment script should be forced to execute" + } + }, + "rbacRolesNeededOnKV": { + "type": "string", + "defaultValue": "a4417e6f-fecd-4de8-b567-7b0420556985", + "metadata": { + "description": "The RoleDefinitionId required for the DeploymentScript resource to interact with KeyVault" + } + }, + "useExistingManagedIdentity": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Does the Managed Identity already exists, or should be created" + } + }, + "managedIdentityName": { + "type": "string", + "defaultValue": "[format('id-KeyVaultCertificateCreator-{0}', parameters('location'))]", + "metadata": { + "description": "Name of the Managed Identity resource" + } + }, + "existingManagedIdentitySubId": { + "type": "string", + "defaultValue": "[subscription().subscriptionId]", + "metadata": { + "description": "For an existing Managed Identity, the Subscription Id it is located in" + } + }, + "existingManagedIdentityResourceGroupName": { + "type": "string", + "defaultValue": "[resourceGroup().name]", + "metadata": { + "description": "For an existing Managed Identity, the Resource Group it is located in" + } + }, + "certificateNames": { + "type": "array", + "metadata": { + "description": "The names of the certificate to create. Use when creating many certificates." + } + }, + "certificateCommonNames": { + "type": "array", + "defaultValue": "[parameters('certificateNames')]", + "metadata": { + "description": "The common names of the certificate to create. Use when creating many certificates." + } + }, + "initialScriptDelay": { + "type": "string", + "defaultValue": "0", + "metadata": { + "description": "A delay before the script import operation starts. Primarily to allow Azure AAD Role Assignments to propagate" + } + }, + "cleanupPreference": { + "type": "string", + "defaultValue": "OnSuccess", + "allowedValues": [ + "OnSuccess", + "OnExpiration", + "Always" + ], + "metadata": { + "description": "When the script resource is cleaned up" + } + }, + "issuerName": { + "type": "string", + "defaultValue": "Self", + "metadata": { + "description": "Self, or user defined {IssuerName} for certificate signing" + } + }, + "issuerProvider": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Certificate Issuer Provider, DigiCert, GlobalSign, or internal options may be used." + } + }, + "disabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Create certificate in disabled state. Default: false" + } + }, + "accountId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Account ID of Certificate Issuer Account" + } + }, + "issuerPassword": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Password of Certificate Issuer Account" + } + }, + "organizationId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Organization ID of Certificate Issuer Account" + } + }, + "isCrossTenant": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Override this parameter if using this in cross tenant scenarios" + } + }, + "reuseKey": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "The default policy might cause errors about CSR being used before, so set this to false if that happens" + } + }, + "validity": { + "type": "int", + "defaultValue": 12, + "minValue": 1, + "maxValue": 1200, + "metadata": { + "description": "Optional. Override default validityInMonths 12 value" + } + }, + "performRoleAssignment": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Set to false to disable role assignments within this module. Default: true" + } + } + }, + "variables": { + "$fxv#0": "#!/bin/bash\nset -e\ninitialDelay=\"${initialDelay:-5}\"\nretryMax=\"${retryMax:-5}\"\ncertName=\"${certName:-default-cert}\"\ncertCommonName=\"${certCommonName:-default}\"\nvalidity=\"${validity:-12}\"\nakvName=\"${akvName:-keyvault}\"\nissuerName=\"${issuerName:-}\"\nreuseKey=\"${reuseKey:-true}\"\nretrySleep=\"${retrySleep:-5}\"\n\necho \"Waiting on Identity RBAC replication (\\\"$initialDelay\\\")\"\nsleep \"$initialDelay\"\n\n#Retry loop to catch errors (usually RBAC delays)\nretryLoopCount=0\nuntil [ \"$retryLoopCount\" -ge \"$retryMax\" ]\ndo\n echo \"Creating AKV Cert $certName with CN $certCommonName (attempt $retryLoopCount)...\"\n\n if [ -z \"$issuerName\" ] || [ -z \"$issuerProvider\" ]; then\n policy=$(az keyvault certificate get-default-policy \\\n | sed -e s/\\\"validityInMonths\\\":\\ 12/\\\"validityInMonths\\\":\\ \"${validity}\"/g \\\n | sed -e s/CN=CLIGetDefaultPolicy/CN=\"${certCommonName}\"/g )\n else\n if [ \"$issuerProvider\" == \"DigiCert\" ] || [ \"$issuerProvider\" == \"GlobalCert\" ]; then\n az keyvault certificate issuer create \\\n --vault-name \"$akvName\" \\\n --issuer-name \"$issuerName\" \\\n --provider-name \"$issuerProvider\" \\\n --account-id \"$accountId\" \\\n --password \"$issuerPassword\" \\\n --organizatiion-id \"$organizationId\"\n else\n az keyvault certificate issuer create \\\n --vault-name \"$akvName\" \\\n --issuer-name \"$issuerName\" \\\n --provider-name \"$issuerProvider\"\n fi\n policy=$(az keyvault certificate get-default-policy \\\n | sed -e s/\\\"validityInMonths\\\":\\ 12/\\\"validityInMonths\\\":\\ \"${validity}\"/g \\\n | sed -e s/CN=CLIGetDefaultPolicy/CN=\"${certCommonName}\"/g \\\n | sed -e s/\\\"name\\\":\\ \\\"Self\\\"/\\\"name\\\":\\ \\\"\"${issuerName}\"\\\"/g \\\n | sed -e s/\\\"reuseKey\\\":\\ true/\\\"reuseKey\\\":\\ \"${reuseKey}\"/g )\n fi\n az keyvault certificate create \\\n --vault-name \"$akvName\" \\\n -n \"$certName\" \\\n -p \"$policy\" \\\n --disabled \"$disabled\" \\\n && break\n\n sleep \"$retrySleep\"\n retryLoopCount=$((retryLoopCount+1))\ndone\n\necho \"Getting Certificate $certName\";\nretryLoopCount=0\ncreatedCert=$(az keyvault certificate show -n \"$certName\" --vault-name \"$akvName\" -o json)\nwhile [ -z \"$(echo \"$createdCert\" | jq -r '.x509ThumbprintHex')\" ] && [ $retryLoopCount -lt \"$retryMax\" ]\ndo\n echo \"Waiting for cert creation (attempt $retryLoopCount)...\"\n sleep $retrySleep\n createdCert=$(az keyvault certificate show -n $certName --vault-name $akvName -o json)\n retryLoopCount=$((retryLoopCount+1))\ndone\n\nunversionedSecretId=$(echo $createdCert | jq -r \".sid\" | cut -d'/' -f-5) # remove the version from the url;\njsonOutputString=$(echo $createdCert | jq --arg usid $unversionedSecretId '{name: .name ,certSecretId: {versioned: .sid, unversioned: $usid }, thumbprint: .x509Thumbprint, thumbprintHex: .x509ThumbprintHex}')\necho $jsonOutputString > $AZ_SCRIPTS_OUTPUT_PATH", + "delegatedManagedIdentityResourceId": "[if(parameters('useExistingManagedIdentity'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')))]" + }, + "resources": [ + { + "condition": "[not(parameters('useExistingManagedIdentity'))]", + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2018-11-30", + "name": "[parameters('managedIdentityName')]", + "location": "[parameters('location')]", + "metadata": { + "description": "A new managed identity that will be created in this Resource Group, this is the default option" + } + }, + { + "condition": "[parameters('performRoleAssignment')]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('kvName'))]", + "name": "[guid(resourceId('Microsoft.KeyVault/vaults', parameters('kvName')), parameters('rbacRolesNeededOnKV'), parameters('managedIdentityName'), string(parameters('useExistingManagedIdentity')))]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('rbacRolesNeededOnKV'))]", + "principalId": "[if(parameters('useExistingManagedIdentity'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), '2018-11-30').principalId, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), '2018-11-30').principalId)]", + "principalType": "ServicePrincipal", + "delegatedManagedIdentityResourceId": "[if(parameters('isCrossTenant'), variables('delegatedManagedIdentityResourceId'), null())]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))]" + ] + }, + { + "copy": { + "name": "createImportCerts", + "count": "[length(parameters('certificateNames'))]" + }, + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2020-10-01", + "name": "[format('create-cert-{0}-{1}', parameters('kvName'), replace(replace(parameters('certificateNames')[copyIndex()], ':', ''), '/', '-'))]", + "location": "[parameters('location')]", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', if(parameters('useExistingManagedIdentity'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))))]": {} + } + }, + "kind": "AzureCLI", + "properties": { + "forceUpdateTag": "[parameters('forceUpdateTag')]", + "azCliVersion": "2.35.0", + "timeout": "PT10M", + "retentionInterval": "P1D", + "environmentVariables": [ + { + "name": "akvName", + "value": "[parameters('kvName')]" + }, + { + "name": "certName", + "value": "[parameters('certificateNames')[copyIndex()]]" + }, + { + "name": "certCommonName", + "value": "[parameters('certificateCommonNames')[copyIndex()]]" + }, + { + "name": "initialDelay", + "value": "[parameters('initialScriptDelay')]" + }, + { + "name": "issuerName", + "value": "[parameters('issuerName')]" + }, + { + "name": "issuerProvider", + "value": "[parameters('issuerProvider')]" + }, + { + "name": "disabled", + "value": "[toLower(string(parameters('disabled')))]" + }, + { + "name": "retryMax", + "value": "10" + }, + { + "name": "retrySleep", + "value": "5s" + }, + { + "name": "accountId", + "value": "[parameters('accountId')]" + }, + { + "name": "issuerPassword", + "secureValue": "[parameters('issuerPassword')]" + }, + { + "name": "organizationId", + "value": "[parameters('organizationId')]" + }, + { + "name": "reuseKey", + "value": "[toLower(string(parameters('reuseKey')))]" + }, + { + "name": "validity", + "value": "[string(parameters('validity'))]" + } + ], + "scriptContent": "[variables('$fxv#0')]", + "cleanupPreference": "[parameters('cleanupPreference')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))]", + "[extensionResourceId(resourceId('Microsoft.KeyVault/vaults', parameters('kvName')), 'Microsoft.Authorization/roleAssignments', guid(resourceId('Microsoft.KeyVault/vaults', parameters('kvName')), parameters('rbacRolesNeededOnKV'), parameters('managedIdentityName'), string(parameters('useExistingManagedIdentity'))))]" + ] + } + ], + "outputs": { + "certificateNames": { + "type": "array", + "metadata": { + "description": "Certificate names" + }, + "copy": { + "count": "[length(parameters('certificateNames'))]", + "input": "[createArray(reference(resourceId('Microsoft.Resources/deploymentScripts', format('create-cert-{0}-{1}', parameters('kvName'), replace(replace(parameters('certificateNames')[copyIndex()], ':', ''), '/', '-'))), '2020-10-01').outputs.name)]" + } + }, + "certificateSecretIds": { + "type": "array", + "metadata": { + "description": "KeyVault secret ids to the created version" + }, + "copy": { + "count": "[length(parameters('certificateNames'))]", + "input": "[createArray(reference(resourceId('Microsoft.Resources/deploymentScripts', format('create-cert-{0}-{1}', parameters('kvName'), replace(replace(parameters('certificateNames')[copyIndex()], ':', ''), '/', '-'))), '2020-10-01').outputs.certSecretId.versioned)]" + } + }, + "certificateSecretIdUnversioneds": { + "type": "array", + "metadata": { + "description": "KeyVault secret ids which uses the unversioned uri" + }, + "copy": { + "count": "[length(parameters('certificateNames'))]", + "input": "[createArray(reference(resourceId('Microsoft.Resources/deploymentScripts', format('create-cert-{0}-{1}', parameters('kvName'), replace(replace(parameters('certificateNames')[copyIndex()], ':', ''), '/', '-'))), '2020-10-01').outputs.certSecretId.unversioned)]" + } + }, + "certificateThumbpints": { + "type": "array", + "metadata": { + "description": "Certificate Thumbprints" + }, + "copy": { + "count": "[length(parameters('certificateNames'))]", + "input": "[createArray(reference(resourceId('Microsoft.Resources/deploymentScripts', format('create-cert-{0}-{1}', parameters('kvName'), replace(replace(parameters('certificateNames')[copyIndex()], ':', ''), '/', '-'))), '2020-10-01').outputs.thumbprint)]" + } + }, + "certificateThumbprintHexs": { + "type": "array", + "metadata": { + "description": "Certificate Thumbprints (in hex)" + }, + "copy": { + "count": "[length(parameters('certificateNames'))]", + "input": "[createArray(reference(resourceId('Microsoft.Resources/deploymentScripts', format('create-cert-{0}-{1}', parameters('kvName'), replace(replace(parameters('certificateNames')[copyIndex()], ':', ''), '/', '-'))), '2020-10-01').outputs.thumbprintHex)]" + } + } + } +} \ No newline at end of file diff --git a/bicep/modules/script-kv-certificate/script.sh b/bicep/modules/script-kv-certificate/script.sh new file mode 100644 index 00000000..93838ad5 --- /dev/null +++ b/bicep/modules/script-kv-certificate/script.sh @@ -0,0 +1,71 @@ +#!/bin/bash +set -e +initialDelay="${initialDelay:-5}" +retryMax="${retryMax:-5}" +certName="${certName:-default-cert}" +certCommonName="${certCommonName:-default}" +validity="${validity:-12}" +akvName="${akvName:-keyvault}" +issuerName="${issuerName:-}" +reuseKey="${reuseKey:-true}" +retrySleep="${retrySleep:-5}" + +echo "Waiting on Identity RBAC replication (\"$initialDelay\")" +sleep "$initialDelay" + +#Retry loop to catch errors (usually RBAC delays) +retryLoopCount=0 +until [ "$retryLoopCount" -ge "$retryMax" ] +do + echo "Creating AKV Cert $certName with CN $certCommonName (attempt $retryLoopCount)..." + + if [ -z "$issuerName" ] || [ -z "$issuerProvider" ]; then + policy=$(az keyvault certificate get-default-policy \ + | sed -e s/\"validityInMonths\":\ 12/\"validityInMonths\":\ "${validity}"/g \ + | sed -e s/CN=CLIGetDefaultPolicy/CN="${certCommonName}"/g ) + else + if [ "$issuerProvider" == "DigiCert" ] || [ "$issuerProvider" == "GlobalCert" ]; then + az keyvault certificate issuer create \ + --vault-name "$akvName" \ + --issuer-name "$issuerName" \ + --provider-name "$issuerProvider" \ + --account-id "$accountId" \ + --password "$issuerPassword" \ + --organizatiion-id "$organizationId" + else + az keyvault certificate issuer create \ + --vault-name "$akvName" \ + --issuer-name "$issuerName" \ + --provider-name "$issuerProvider" + fi + policy=$(az keyvault certificate get-default-policy \ + | sed -e s/\"validityInMonths\":\ 12/\"validityInMonths\":\ "${validity}"/g \ + | sed -e s/CN=CLIGetDefaultPolicy/CN="${certCommonName}"/g \ + | sed -e s/\"name\":\ \"Self\"/\"name\":\ \""${issuerName}"\"/g \ + | sed -e s/\"reuseKey\":\ true/\"reuseKey\":\ "${reuseKey}"/g ) + fi + az keyvault certificate create \ + --vault-name "$akvName" \ + -n "$certName" \ + -p "$policy" \ + --disabled "$disabled" \ + && break + + sleep "$retrySleep" + retryLoopCount=$((retryLoopCount+1)) +done + +echo "Getting Certificate $certName"; +retryLoopCount=0 +createdCert=$(az keyvault certificate show -n "$certName" --vault-name "$akvName" -o json) +while [ -z "$(echo "$createdCert" | jq -r '.x509ThumbprintHex')" ] && [ $retryLoopCount -lt "$retryMax" ] +do + echo "Waiting for cert creation (attempt $retryLoopCount)..." + sleep $retrySleep + createdCert=$(az keyvault certificate show -n $certName --vault-name $akvName -o json) + retryLoopCount=$((retryLoopCount+1)) +done + +unversionedSecretId=$(echo $createdCert | jq -r ".sid" | cut -d'/' -f-5) # remove the version from the url; +jsonOutputString=$(echo $createdCert | jq --arg usid $unversionedSecretId '{name: .name ,certSecretId: {versioned: .sid, unversioned: $usid }, thumbprint: .x509Thumbprint, thumbprintHex: .x509ThumbprintHex}') +echo $jsonOutputString > $AZ_SCRIPTS_OUTPUT_PATH \ No newline at end of file diff --git a/bicep/modules/script-kv-certificate/test/main.test.bicep b/bicep/modules/script-kv-certificate/test/main.test.bicep new file mode 100644 index 00000000..5bd3e973 --- /dev/null +++ b/bicep/modules/script-kv-certificate/test/main.test.bicep @@ -0,0 +1,104 @@ +targetScope = 'resourceGroup' + +@minLength(3) +@maxLength(10) +@description('Used to name all resources') +param resourceName string + +@description('Registry Location.') +param location string = resourceGroup().location + +//Prerequisites +module identity '../../user-managed-identity/main.bicep' = { + name: 'user-managed-identity' + params: { + resourceName: resourceName + location: location + } +} + +module kv '../../azure-keyvault/main.bicep' = { + name: 'azure_keyvault' + params: { + resourceName: resourceName + location: location + secretsObject: { secrets: [] } + + // Add Role Assignment + roleAssignments: [ + { + roleDefinitionIdOrName: 'Key Vault Administrator' + principals: [ + { + id: identity.outputs.principalId + } + ] + principalType: 'ServicePrincipal' + } + ] + } +} + +//Test 1. Just a single certificate +module kvCertSingle '../main.bicep' = { + name: 'kvCertSingle' + params: { + kvName: kv.outputs.name + location: location + + useExistingManagedIdentity: true + managedIdentityName: identity.outputs.name + existingManagedIdentitySubId: subscription().subscriptionId + existingManagedIdentityResourceGroupName:resourceGroup().name + + certificateNames: [ 'mysingleapp' ] + certificateCommonNames: [ 'mysingleapp.mydomain.local' ] + validity: 11 + disabled: true + } +} +output singleSecretId string = kvCertSingle.outputs.certificateSecretIds[0][0] +output singleThumbprint string = kvCertSingle.outputs.certificateThumbprintHexs[0][0] + +//Test 2. Array of certificates +var certificateNames = [ + 'myapp' + 'myotherapp' +] + +module kvCertMultiple '../main.bicep' = { + name: 'kvCertMultiple-${uniqueString(kv.name)}' + params: { + kvName: kv.name + location: location + + useExistingManagedIdentity: true + managedIdentityName: identity.outputs.name + existingManagedIdentitySubId: subscription().subscriptionId + existingManagedIdentityResourceGroupName:resourceGroup().name + + certificateNames: certificateNames + initialScriptDelay: '0' + validity: 24 + } +} + +// Test 3. Test a signed cert +// module akvCertSigned '../main.bicep' = { +// name: 'akvCertSigned' +// params: { +// akvName: akv.name +// location: location +// certificateName: 'mysignedcert' +// certificateCommonName: 'sample-cert.gaming.azure.com' +// issuerName: 'Signed' +// issuerProvider: 'OneCertV2-PublicCA' +// } +// } + +@description('Array of info from each Certificate') +output createdCertificates array = [for (certificateName, i) in certificateNames: { + certificateName: certificateName + certificateSecretId: kvCertMultiple.outputs.certificateSecretIds[i] + certificateThumbprint: kvCertMultiple.outputs.certificateThumbprintHexs[i] +}] diff --git a/bicep/modules/script-kv-certificate/test/parameters.json b/bicep/modules/script-kv-certificate/test/parameters.json new file mode 100644 index 00000000..4d4c4d1d --- /dev/null +++ b/bicep/modules/script-kv-certificate/test/parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "template": "../main.bicep" + }, + "parameters": { + "location": { + "value": "southcentralus" + }, + "resourceName": { + "value": "adme" + } + } +} \ No newline at end of file diff --git a/bicep/modules/script-kv-certificate/version.json b/bicep/modules/script-kv-certificate/version.json new file mode 100644 index 00000000..e215964e --- /dev/null +++ b/bicep/modules/script-kv-certificate/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "3.4", + "pathFilters": [ + "./main.json" + ] +} \ No newline at end of file diff --git a/bicep/modules/virtual_machine.bicep b/bicep/modules/virtual_machine.bicep new file mode 100644 index 00000000..19baadf9 --- /dev/null +++ b/bicep/modules/virtual_machine.bicep @@ -0,0 +1,175 @@ +// Parameters +@description('Specifies the name of the virtual machine.') +param vmName string = 'simpleVM' + +@description('Specifies the size of the virtual machine.') +param vmSize string = 'Standard_D2s_v3' + +@description('Specifies the resource id of the subnet hosting the virtual machine.') +param vmSubnetId string + +@description('Specifies the image publisher of the disk image used to create the virtual machine.') +param imagePublisher string = 'Canonical' + +@description('Specifies the offer of the platform image or marketplace image used to create the virtual machine.') +param imageOffer string = '0001-com-ubuntu-server-jammy' + +@description('Specifies the Ubuntu version for the VM. This will pick a fully patched image of this given Ubuntu version.') +param imageSku string = '22_04-lts-gen2' + +@description('Specifies the type of authentication when accessing the Virtual Machine. SSH key is recommended.') +@allowed([ + 'sshPublicKey' + 'password' +]) +param authenticationType string = 'password' + +@description('Specifies the name of the administrator account of the virtual machine.') +param vmAdminUsername string + +@description('Specifies the SSH Key or password for the virtual machine. SSH key is recommended.') +@secure() +param vmAdminPasswordOrKey string + +@description('Specifies the storage account type for OS and data disk.') +@allowed([ + 'Premium_LRS' + 'StandardSSD_LRS' + 'Standard_LRS' + 'UltraSSD_LRS' +]) +param diskStorageAccountType string = 'Standard_LRS' + +@description('Specifies the number of data disks of the virtual machine.') +@minValue(0) +@maxValue(64) +param numDataDisks int = 0 + +@description('Specifies the size in GB of the OS disk of the VM.') +param osDiskSize int = 30 + +@description('Specifies the size in GB of the OS disk of the virtual machine.') +param dataDiskSize int = 50 + +@description('Specifies the caching requirements for the data disks.') +param dataDiskCaching string = 'ReadWrite' + +@description('Specifies the name of the Log Analytics workspace.') +param workspaceName string + +@description('Specifies the location.') +param location string = resourceGroup().location + +@description('Specifies the resource tags.') +param tags object + +// Variables +var vmNicName = '${vmName}Nic' +var linuxConfiguration = { + disablePasswordAuthentication: true + ssh: { + publicKeys: [ + { + path: '/home/${vmAdminUsername}/.ssh/authorized_keys' + keyData: vmAdminPasswordOrKey + } + ] + } + provisionVMAgent: true +} + +// Resources +resource virtualMachineNic 'Microsoft.Network/networkInterfaces@2021-08-01' = { + name: vmNicName + location: location + tags: tags + properties: { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: vmSubnetId + } + } + } + ] + } +} + + + +resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-11-01' = { + name: vmName + location: location + tags: tags + properties: { + hardwareProfile: { + vmSize: vmSize + } + osProfile: { + computerName: vmName + adminUsername: vmAdminUsername + adminPassword: vmAdminPasswordOrKey + linuxConfiguration: (authenticationType == 'password') ? null : linuxConfiguration + } + storageProfile: { + imageReference: { + publisher: imagePublisher + offer: imageOffer + sku: imageSku + version: 'latest' + } + osDisk: { + name: '${vmName}_OSDisk' + caching: 'ReadWrite' + createOption: 'FromImage' + diskSizeGB: osDiskSize + managedDisk: { + storageAccountType: diskStorageAccountType + } + } + dataDisks: [for j in range(0, numDataDisks): { + caching: dataDiskCaching + diskSizeGB: dataDiskSize + lun: j + name: '${vmName}-DataDisk${j}' + createOption: 'Empty' + managedDisk: { + storageAccountType: diskStorageAccountType + } + }] + } + networkProfile: { + networkInterfaces: [ + { + id: virtualMachineNic.id + } + ] + } + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = { + name: workspaceName +} + +resource omsAgentForLinux 'Microsoft.Compute/virtualMachines/extensions@2023-03-01' = { + parent: virtualMachine + name: 'LogAnalytics' + location: location + properties: { + publisher: 'Microsoft.EnterpriseCloud.Monitoring' + type: 'OmsAgentForLinux' + typeHandlerVersion: '1.17' + settings: { + workspaceId: logAnalyticsWorkspace.properties.customerId + stopOnMultipleConnections: false + } + protectedSettings: { + workspaceKey: logAnalyticsWorkspace.listKeys().primarySharedKey //listKeys(logAnalyticsWorkspace.id, logAnalyticsWorkspace.apiVersion).primarySharedKey + } + } +} +