diff --git a/modules/aad/domain-service/README.md b/modules/aad/domain-service/README.md index 1330a6f5ec..673231c2f7 100644 --- a/modules/aad/domain-service/README.md +++ b/modules/aad/domain-service/README.md @@ -29,6 +29,7 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br:bicep/modules/aad.domain-service:1.0.0`. - [Using large parameter set](#example-1-using-large-parameter-set) +- [WAF-aligned](#example-2-waf-aligned) ### Example 1: _Using large parameter set_ @@ -158,6 +159,134 @@ module domainService 'br:bicep/modules/aad.domain-service:1.0.0' = {
+### Example 2: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module domainService 'br:bicep/modules/aad.domain-service:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-aaddswaf'
+ params: {
+ // Required parameters
+ domainName: 'onmicrosoft.com'
+ // Non-required parameters
+ additionalRecipients: [
+ '@noreply.github.com'
+ ]
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "domainName": {
+ "value": "onmicrosoft.com"
+ },
+ // Non-required parameters
+ "additionalRecipients": {
+ "value": [
+ "@noreply.github.com"
+ ]
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "
+ ## Parameters diff --git a/modules/aad/domain-service/tests/e2e/waf-aligned/dependencies.bicep b/modules/aad/domain-service/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..0767cf436a --- /dev/null +++ b/modules/aad/domain-service/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,104 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Deployment Script to create for the Certificate generation.') +param certDeploymentScriptName string + +var certPWSecretName = 'pfxCertificatePassword' +var certSecretName = 'pfxBase64Certificate' +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: null + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${managedIdentity.name}-KeyVault-Admin-RoleAssignment') + scope: keyVault + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') // Key Vault Administrator + principalType: 'ServicePrincipal' + } +} + +resource certDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: certDeploymentScriptName + location: location + kind: 'AzurePowerShell' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + azPowerShellVersion: '3.0' + retentionInterval: 'P1D' + arguments: ' -KeyVaultName "${keyVault.name}" -ResourceGroupName "${resourceGroup().name}" -CertPWSecretName "${certPWSecretName}" -CertSecretName "${certSecretName}"' + scriptContent: loadTextContent('../../../../../.shared/.scripts/Set-PfxCertificateInKeyVault.ps1') + } +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id + +@description('The name of the certification password secret.') +output certPWSecretName string = certPWSecretName + +@description('The name of the certification secret.') +output certSecretName string = certSecretName + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/aad/domain-service/tests/e2e/waf-aligned/main.test.bicep b/modules/aad/domain-service/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..605f339c95 --- /dev/null +++ b/modules/aad/domain-service/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,109 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-aad.domainservices-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'aaddswaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + certDeploymentScriptName: 'dep-${namePrefix}-ds-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: last(split(nestedDependencies.outputs.keyVaultResourceId, '/')) + scope: resourceGroup +} + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + domainName: '${namePrefix}.onmicrosoft.com' + additionalRecipients: [ + '${namePrefix}@noreply.github.com' + ] + diagnosticSettings: [ + { + name: 'customSetting' + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + pfxCertificate: keyVault.getSecret(nestedDependencies.outputs.certSecretName) + pfxCertificatePassword: keyVault.getSecret(nestedDependencies.outputs.certPWSecretName) + replicaSets: [ + { + location: 'WestEurope' + subnetId: nestedDependencies.outputs.subnetResourceId + } + ] + sku: 'Standard' + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/analysis-services/server/README.md b/modules/analysis-services/server/README.md index 73d7a21652..c35c2a2be3 100644 --- a/modules/analysis-services/server/README.md +++ b/modules/analysis-services/server/README.md @@ -29,6 +29,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -240,6 +241,168 @@ module server 'br:bicep/modules/analysis-services.server:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module server 'br:bicep/modules/analysis-services.server:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-asswaf'
+ params: {
+ // Required parameters
+ name: 'asswaf'
+ // Non-required parameters
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "asswaf"
+ },
+ // Non-required parameters
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "
+ ## Parameters diff --git a/modules/analysis-services/server/tests/e2e/waf-aligned/dependencies.bicep b/modules/analysis-services/server/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..29b9641692 --- /dev/null +++ b/modules/analysis-services/server/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Required. The name of the managed identity to create.') +param managedIdentityName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created managed identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/analysis-services/server/tests/e2e/waf-aligned/main.test.bicep b/modules/analysis-services/server/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..7d160d3715 --- /dev/null +++ b/modules/analysis-services/server/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,120 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-analysisservices.servers-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'asswaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}azsa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + skuName: 'S0' + skuCapacity: 1 + firewallSettings: { + firewallRules: [ + { + firewallRuleName: 'AllowFromAll' + rangeStart: '0.0.0.0' + rangeEnd: '255.255.255.255' + } + ] + enablePowerBIService: true + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + logCategoriesAndGroups: [ + { + category: 'Engine' + } + { + category: 'Service' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/api-management/service/README.md b/modules/api-management/service/README.md index 49ae4a583a..64ee78c465 100644 --- a/modules/api-management/service/README.md +++ b/modules/api-management/service/README.md @@ -44,6 +44,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -471,6 +472,376 @@ module service 'br:bicep/modules/api-management.service:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module service 'br:bicep/modules/api-management.service:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-apiswaf'
+ params: {
+ // Required parameters
+ name: 'apiswaf001'
+ publisherEmail: 'apimgmt-noreply@mail.windowsazure.com'
+ publisherName: 'az-amorg-x-001'
+ // Non-required parameters
+ apis: [
+ {
+ apiVersionSet: {
+ name: 'echo-version-set'
+ properties: {
+ description: 'echo-version-set'
+ displayName: 'echo-version-set'
+ versioningScheme: 'Segment'
+ }
+ }
+ displayName: 'Echo API'
+ name: 'echo-api'
+ path: 'echo'
+ serviceUrl: 'http://echoapi.cloudapp.net/api'
+ }
+ ]
+ authorizationServers: {
+ secureList: [
+ {
+ authorizationEndpoint: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "apiswaf001"
+ },
+ "publisherEmail": {
+ "value": "apimgmt-noreply@mail.windowsazure.com"
+ },
+ "publisherName": {
+ "value": "az-amorg-x-001"
+ },
+ // Non-required parameters
+ "apis": {
+ "value": [
+ {
+ "apiVersionSet": {
+ "name": "echo-version-set",
+ "properties": {
+ "description": "echo-version-set",
+ "displayName": "echo-version-set",
+ "versioningScheme": "Segment"
+ }
+ },
+ "displayName": "Echo API",
+ "name": "echo-api",
+ "path": "echo",
+ "serviceUrl": "http://echoapi.cloudapp.net/api"
+ }
+ ]
+ },
+ "authorizationServers": {
+ "value": {
+ "secureList": [
+ {
+ "authorizationEndpoint": "
+
## Parameters
diff --git a/modules/api-management/service/tests/e2e/waf-aligned/dependencies.bicep b/modules/api-management/service/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..bd63a95634
--- /dev/null
+++ b/modules/api-management/service/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,16 @@
+@description('Required. The name of the managed identity to create.')
+param managedIdentityName string
+
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created managed identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
diff --git a/modules/api-management/service/tests/e2e/waf-aligned/main.test.bicep b/modules/api-management/service/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..e6246837b8
--- /dev/null
+++ b/modules/api-management/service/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,219 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-apimanagement.service-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'apiswaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+@description('Optional. The secret to leverage for authorization server authentication.')
+@secure()
+param customSecret string = newGuid()
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}azsa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ publisherEmail: 'apimgmt-noreply@mail.windowsazure.com'
+ publisherName: '${namePrefix}-az-amorg-x-001'
+ apis: [
+ {
+ apiVersionSet: {
+ name: 'echo-version-set'
+ properties: {
+ description: 'echo-version-set'
+ displayName: 'echo-version-set'
+ versioningScheme: 'Segment'
+ }
+ }
+ displayName: 'Echo API'
+ name: 'echo-api'
+ path: 'echo'
+ serviceUrl: 'http://echoapi.cloudapp.net/api'
+ }
+ ]
+ authorizationServers: {
+ secureList: [
+ {
+ authorizationEndpoint: '${environment().authentication.loginEndpoint}651b43ce-ccb8-4301-b551-b04dd872d401/oauth2/v2.0/authorize'
+ clientId: 'apimclientid'
+ clientSecret: customSecret
+ clientRegistrationEndpoint: 'http://localhost'
+ grantTypes: [
+ 'authorizationCode'
+ ]
+ name: 'AuthServer1'
+ tokenEndpoint: '${environment().authentication.loginEndpoint}651b43ce-ccb8-4301-b551-b04dd872d401/oauth2/v2.0/token'
+ }
+ ]
+ }
+ backends: [
+ {
+ name: 'backend'
+ tls: {
+ validateCertificateChain: false
+ validateCertificateName: false
+ }
+ url: 'http://echoapi.cloudapp.net/api'
+ }
+ ]
+ caches: [
+ {
+ connectionString: 'connectionstringtest'
+ name: 'westeurope'
+ useFromLocation: 'westeurope'
+ }
+ ]
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ identityProviders: [
+ {
+ name: 'aadProvider'
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ namedValues: [
+ {
+ displayName: 'apimkey'
+ name: 'apimkey'
+ secret: true
+ }
+ ]
+ policies: [
+ {
+ format: 'xml'
+ value: '
+### Example 5: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module configurationStore 'br:bicep/modules/app-configuration.configuration-store:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-accwaf'
+ params: {
+ // Required parameters
+ name: 'accwaf001'
+ // Non-required parameters
+ createMode: 'Default'
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "accwaf001"
+ },
+ // Non-required parameters
+ "createMode": {
+ "value": "Default"
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "
+ ## Parameters diff --git a/modules/app-configuration/configuration-store/tests/e2e/waf-aligned/dependencies.bicep b/modules/app-configuration/configuration-store/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..bd63a95634 --- /dev/null +++ b/modules/app-configuration/configuration-store/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,16 @@ +@description('Required. The name of the managed identity to create.') +param managedIdentityName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created managed identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id diff --git a/modules/app-configuration/configuration-store/tests/e2e/waf-aligned/main.test.bicep b/modules/app-configuration/configuration-store/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..22770e01be --- /dev/null +++ b/modules/app-configuration/configuration-store/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,124 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-appconfiguration.configurationstores-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'accwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + createMode: 'Default' + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + disableLocalAuth: false + enablePurgeProtection: false + keyValues: [ + { + contentType: 'contentType' + name: 'keyName' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + value: 'valueName' + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + softDeleteRetentionInDays: 1 + managedIdentities: { + systemAssigned: true + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/app/container-app/README.md b/modules/app/container-app/README.md index 56ef31b6d4..c6ad339911 100644 --- a/modules/app/container-app/README.md +++ b/modules/app/container-app/README.md @@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -279,6 +280,168 @@ module containerApp 'br:bicep/modules/app.container-app:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module containerApp 'br:bicep/modules/app.container-app:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-mcappwaf'
+ params: {
+ // Required parameters
+ containers: [
+ {
+ image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
+ name: 'simple-hello-world-container'
+ probes: [
+ {
+ httpGet: {
+ httpHeaders: [
+ {
+ name: 'Custom-Header'
+ value: 'Awesome'
+ }
+ ]
+ path: '/health'
+ port: 8080
+ }
+ initialDelaySeconds: 3
+ periodSeconds: 3
+ type: 'Liveness'
+ }
+ ]
+ resources: {
+ cpu: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "containers": {
+ "value": [
+ {
+ "image": "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest",
+ "name": "simple-hello-world-container",
+ "probes": [
+ {
+ "httpGet": {
+ "httpHeaders": [
+ {
+ "name": "Custom-Header",
+ "value": "Awesome"
+ }
+ ],
+ "path": "/health",
+ "port": 8080
+ },
+ "initialDelaySeconds": 3,
+ "periodSeconds": 3,
+ "type": "Liveness"
+ }
+ ],
+ "resources": {
+ "cpu": "
+ ## Parameters diff --git a/modules/app/container-app/tests/e2e/waf-aligned/dependencies.bicep b/modules/app/container-app/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..a6700c9d60 --- /dev/null +++ b/modules/app/container-app/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,28 @@ +@description('Required. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Environment for Container Apps to create.') +param managedEnvironmentName string + +@description('Required. The name of the managed identity to create.') +param managedIdentityName string + +resource managedEnvironment 'Microsoft.App/managedEnvironments@2022-10-01' = { + name: managedEnvironmentName + location: location + sku: { + name: 'Consumption' + } + properties: {} +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2022-01-31-preview' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The resource ID of the created Managed Environment.') +output managedEnvironmentResourceId string = managedEnvironment.id diff --git a/modules/app/container-app/tests/e2e/waf-aligned/main.test.bicep b/modules/app/container-app/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..baa721dd00 --- /dev/null +++ b/modules/app/container-app/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,109 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-app.containerApps-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'mcappwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-paramNested' + params: { + location: location + managedEnvironmentName: 'dep-${namePrefix}-menv-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + tags: { + 'hidden-title': 'This is visible in the resource name' + Env: 'test' + } + enableDefaultTelemetry: enableDefaultTelemetry + environmentId: nestedDependencies.outputs.managedEnvironmentResourceId + location: location + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + managedIdentities: { + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + secrets: { + secureList: [ + { + name: 'customtest' + value: guid(deployment().name) + } + ] + } + containers: [ + { + name: 'simple-hello-world-container' + image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + resources: { + // workaround as 'float' values are not supported in Bicep, yet the resource providers expects them. Related issue: https://github.com/Azure/bicep/issues/1386 + cpu: json('0.25') + memory: '0.5Gi' + } + probes: [ + { + type: 'Liveness' + httpGet: { + path: '/health' + port: 8080 + httpHeaders: [ + { + name: 'Custom-Header' + value: 'Awesome' + } + ] + } + initialDelaySeconds: 3 + periodSeconds: 3 + } + ] + } + ] + } +} diff --git a/modules/app/job/README.md b/modules/app/job/README.md index c5d025fad6..042067b52b 100644 --- a/modules/app/job/README.md +++ b/modules/app/job/README.md @@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -329,6 +330,204 @@ module job 'br:bicep/modules/app.job:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module job 'br:bicep/modules/app.job:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-ajwaf'
+ params: {
+ // Required parameters
+ containers: [
+ {
+ image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
+ name: 'simple-hello-world-container'
+ probes: [
+ {
+ httpGet: {
+ httpHeaders: [
+ {
+ name: 'Custom-Header'
+ value: 'Awesome'
+ }
+ ]
+ path: '/health'
+ port: 8080
+ }
+ initialDelaySeconds: 3
+ periodSeconds: 3
+ type: 'Liveness'
+ }
+ ]
+ resources: {
+ cpu: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "containers": {
+ "value": [
+ {
+ "image": "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest",
+ "name": "simple-hello-world-container",
+ "probes": [
+ {
+ "httpGet": {
+ "httpHeaders": [
+ {
+ "name": "Custom-Header",
+ "value": "Awesome"
+ }
+ ],
+ "path": "/health",
+ "port": 8080
+ },
+ "initialDelaySeconds": 3,
+ "periodSeconds": 3,
+ "type": "Liveness"
+ }
+ ],
+ "resources": {
+ "cpu": "
+ ## Parameters diff --git a/modules/app/job/tests/e2e/waf-aligned/dependencies.bicep b/modules/app/job/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..b03d4aca93 --- /dev/null +++ b/modules/app/job/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,40 @@ +@description('Required. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Environment for Container Apps to create.') +param managedEnvironmentName string + +@description('Required. The name of the managed identity to create.') +param managedIdentityName string + +@description('Required. The name of the workload profile to create.') +param workloadProfileName string + +resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = { + name: managedEnvironmentName + location: location + properties: { + workloadProfiles: [ + { + name: workloadProfileName + workloadProfileType: 'D4' + maximumCount: 1 + minimumCount: 1 + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2022-01-31-preview' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The resource ID of the created Managed Environment.') +output managedEnvironmentResourceId string = managedEnvironment.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/app/job/tests/e2e/waf-aligned/main.test.bicep b/modules/app/job/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..267c39bb21 --- /dev/null +++ b/modules/app/job/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,124 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-app.job-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'ajwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-paramNested' + params: { + location: location + managedEnvironmentName: 'dep-${namePrefix}-menv-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + workloadProfileName: serviceShort + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + tags: { + 'hidden-title': 'This is visible in the resource name' + Env: 'test' + } + enableDefaultTelemetry: enableDefaultTelemetry + environmentId: nestedDependencies.outputs.managedEnvironmentResourceId + workloadProfileName: serviceShort + location: location + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + managedIdentities: { + systemAssigned: true + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + secrets: { + secureList: [ + { + name: 'customtest' + value: guid(deployment().name) + } + ] + } + triggerType: 'Manual' + manualTriggerConfig: { + replicaCompletionCount: 1 + parallelism: 1 + } + containers: [ + { + name: 'simple-hello-world-container' + image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + resources: { + // workaround as 'float' values are not supported in Bicep, yet the resource providers expects them. Related issue: https://github.com/Azure/bicep/issues/1386 + cpu: json('0.25') + memory: '0.5Gi' + } + probes: [ + { + type: 'Liveness' + httpGet: { + path: '/health' + port: 8080 + httpHeaders: [ + { + name: 'Custom-Header' + value: 'Awesome' + } + ] + } + initialDelaySeconds: 3 + periodSeconds: 3 + } + ] + } + ] + roleAssignments: [ + { + principalId: nestedDependencies.outputs.managedIdentityResourceId + roleDefinitionIdOrName: 'ContainerApp Reader' + principalType: 'ServicePrincipal' + } + ] + } +} diff --git a/modules/app/managed-environment/README.md b/modules/app/managed-environment/README.md index 40ec6dfd7e..d222427925 100644 --- a/modules/app/managed-environment/README.md +++ b/modules/app/managed-environment/README.md @@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -179,6 +180,106 @@ module managedEnvironment 'br:bicep/modules/app.managed-environment:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module managedEnvironment 'br:bicep/modules/app.managed-environment:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-amewaf'
+ params: {
+ // Required parameters
+ enableDefaultTelemetry: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "enableDefaultTelemetry": {
+ "value": "
+ ## Parameters diff --git a/modules/app/managed-environment/tests/e2e/waf-aligned/dependencies.bicep b/modules/app/managed-environment/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..f61380acc4 --- /dev/null +++ b/modules/app/managed-environment/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,51 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Log Analytics Workspace to create.') +param logAnalyticsWorkspaceName string + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +var addressPrefix = '10.0.0.0/16' + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { + name: logAnalyticsWorkspaceName + location: location + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } + +} + +@description('The resource ID of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id diff --git a/modules/app/managed-environment/tests/e2e/waf-aligned/main.test.bicep b/modules/app/managed-environment/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..49d64c4d2c --- /dev/null +++ b/modules/app/managed-environment/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,72 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-app.managedenvironments-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'amewaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-paramNested' + params: { + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + logAnalyticsWorkspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + location: location + skuName: 'Consumption' + internal: true + dockerBridgeCidr: '172.16.0.1/28' + platformReservedCidr: '172.17.17.0/24' + platformReservedDnsIP: '172.17.17.17' + infrastructureSubnetId: nestedDependencies.outputs.subnetResourceId + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Env: 'test' + } + } +} diff --git a/modules/authorization/lock/README.md b/modules/authorization/lock/README.md index 5d3f67c3e0..7e2543aee3 100644 --- a/modules/authorization/lock/README.md +++ b/modules/authorization/lock/README.md @@ -25,6 +25,7 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br:bicep/modules/authorization.lock:1.0.0`. - [Using large parameter set](#example-1-using-large-parameter-set) +- [WAF-aligned](#example-2-waf-aligned) ### Example 1: _Using large parameter set_ @@ -82,6 +83,62 @@ module lock 'br:bicep/modules/authorization.lock:1.0.0' = {
+### Example 2: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module lock 'br:bicep/modules/authorization.lock:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-alwaf'
+ params: {
+ // Required parameters
+ level: 'CanNotDelete'
+ // Non-required parameters
+ enableDefaultTelemetry: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "level": {
+ "value": "CanNotDelete"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "
+ ## Parameters diff --git a/modules/authorization/lock/tests/e2e/waf-aligned/main.test.bicep b/modules/authorization/lock/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..0ed75a7621 --- /dev/null +++ b/modules/authorization/lock/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,49 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.locks-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'alwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + level: 'CanNotDelete' + resourceGroupName: resourceGroup.name + subscriptionId: subscription().subscriptionId + } +} diff --git a/modules/automation/automation-account/README.md b/modules/automation/automation-account/README.md index 49340e030c..42e498b90a 100644 --- a/modules/automation/automation-account/README.md +++ b/modules/automation/automation-account/README.md @@ -40,6 +40,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Encr](#example-2-encr) - [Using large parameter set](#example-3-using-large-parameter-set) +- [WAF-aligned](#example-4-waf-aligned) ### Example 1: _Using only defaults_ @@ -614,6 +615,462 @@ module automationAccount 'br:bicep/modules/automation.automation-account:1.0.0'
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module automationAccount 'br:bicep/modules/automation.automation-account:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-aawaf'
+ params: {
+ // Required parameters
+ name: 'aawaf001'
+ // Non-required parameters
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "aawaf001"
+ },
+ // Non-required parameters
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "
+ ## Parameters diff --git a/modules/automation/automation-account/tests/e2e/waf-aligned/dependencies.bicep b/modules/automation/automation-account/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..3a979dc83b --- /dev/null +++ b/modules/automation/automation-account/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,90 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.azure-automation.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: null + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id + +@description('The URL of the created Key Vault.') +output keyVaultUrl string = keyVault.properties.vaultUri + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id diff --git a/modules/automation/automation-account/tests/e2e/waf-aligned/main.test.bicep b/modules/automation/automation-account/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..ebff0d4bc1 --- /dev/null +++ b/modules/automation/automation-account/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,261 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-automation.account-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'aawaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + gallerySolutions: [ + { + name: 'Updates' + product: 'OMSGallery' + publisher: 'Microsoft' + } + ] + jobSchedules: [ + { + runbookName: 'TestRunbook' + scheduleName: 'TestSchedule' + } + ] + disableLocalAuth: true + linkedWorkspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + modules: [ + { + name: 'PSWindowsUpdate' + uri: 'https://www.powershellgallery.com/api/v2/package' + version: 'latest' + } + ] + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + service: 'Webhook' + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + service: 'DSCAndHybridWorker' + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + runbooks: [ + { + description: 'Test runbook' + name: 'TestRunbook' + type: 'PowerShell' + uri: 'https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.automation/101-automation/scripts/AzureAutomationTutorial.ps1' + version: '1.0.0.0' + } + ] + schedules: [ + { + advancedSchedule: {} + expiryTime: '9999-12-31T13:00' + frequency: 'Hour' + interval: 12 + name: 'TestSchedule' + startTime: '' + timeZone: 'Europe/Berlin' + } + ] + softwareUpdateConfigurations: [ + { + excludeUpdates: [ + '123456' + ] + frequency: 'Month' + includeUpdates: [ + '654321' + ] + interval: 1 + maintenanceWindow: 'PT4H' + monthlyOccurrences: [ + { + day: 'Friday' + occurrence: 3 + } + ] + name: 'Windows_ZeroDay' + operatingSystem: 'Windows' + rebootSetting: 'IfRequired' + scopeByTags: { + Update: [ + 'Automatic-Wave1' + ] + } + startTime: '22:00' + updateClassifications: [ + 'Critical' + 'Definition' + 'FeaturePack' + 'Security' + 'ServicePack' + 'Tools' + 'UpdateRollup' + 'Updates' + ] + } + { + excludeUpdates: [ + 'icacls' + ] + frequency: 'OneTime' + includeUpdates: [ + 'kernel' + ] + maintenanceWindow: 'PT4H' + name: 'Linux_ZeroDay' + operatingSystem: 'Linux' + rebootSetting: 'IfRequired' + startTime: '22:00' + updateClassifications: [ + 'Critical' + 'Other' + 'Security' + ] + } + ] + managedIdentities: { + systemAssigned: true + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + variables: [ + { + description: 'TestStringDescription' + name: 'TestString' + value: '\'TestString\'' + } + { + description: 'TestIntegerDescription' + name: 'TestInteger' + value: '500' + } + { + description: 'TestBooleanDescription' + name: 'TestBoolean' + value: 'false' + } + { + description: 'TestDateTimeDescription' + isEncrypted: false + name: 'TestDateTime' + value: '\'\\/Date(1637934042656)\\/\'' + } + { + description: 'TestEncryptedDescription' + name: 'TestEncryptedVariable' + value: '\'TestEncryptedValue\'' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/batch/batch-account/README.md b/modules/batch/batch-account/README.md index 3a1c9b5e7f..2d71887df9 100644 --- a/modules/batch/batch-account/README.md +++ b/modules/batch/batch-account/README.md @@ -34,6 +34,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Encr](#example-2-encr) - [Using large parameter set](#example-3-using-large-parameter-set) +- [WAF-aligned](#example-4-waf-aligned) ### Example 1: _Using only defaults_ @@ -392,6 +393,188 @@ module batchAccount 'br:bicep/modules/batch.batch-account:1.0.0' = {
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module batchAccount 'br:bicep/modules/batch.batch-account:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-bbawaf'
+ params: {
+ // Required parameters
+ name: 'bbawaf001'
+ storageAccountId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "bbawaf001"
+ },
+ "storageAccountId": {
+ "value": "
+ ## Parameters diff --git a/modules/batch/batch-account/tests/e2e/waf-aligned/dependencies.bicep b/modules/batch/batch-account/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..462e8a5f27 --- /dev/null +++ b/modules/batch/batch-account/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,78 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +var addressPrefix = '10.0.0.0/16' + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.batch.azure.com' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The resource ID of the created Virtual Network Subnet.') +output storageAccountResourceId string = storageAccount.id + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id diff --git a/modules/batch/batch-account/tests/e2e/waf-aligned/main.test.bicep b/modules/batch/batch-account/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..20fbc393af --- /dev/null +++ b/modules/batch/batch-account/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,129 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-batch.batchaccounts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'bbawaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + storageAccountName: 'dep${namePrefix}st${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + storageAccountId: nestedDependencies.outputs.storageAccountResourceId + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + poolAllocationMode: 'BatchService' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + privateEndpoints: [ + { + subnetResourceId: nestedDependencies.outputs.subnetResourceId + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + storageAccessIdentity: nestedDependencies.outputs.managedIdentityResourceId + storageAuthenticationMode: 'BatchAccountManagedIdentity' + managedIdentities: { + systemAssigned: true + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/cache/redis-enterprise/README.md b/modules/cache/redis-enterprise/README.md index 50eaf4f856..0c37755f50 100644 --- a/modules/cache/redis-enterprise/README.md +++ b/modules/cache/redis-enterprise/README.md @@ -33,6 +33,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Geo](#example-2-geo) - [Using large parameter set](#example-3-using-large-parameter-set) +- [WAF-aligned](#example-4-waf-aligned) ### Example 1: _Using only defaults_ @@ -397,6 +398,200 @@ module redisEnterprise 'br:bicep/modules/cache.redis-enterprise:1.0.0' = {
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module redisEnterprise 'br:bicep/modules/cache.redis-enterprise:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-crewaf'
+ params: {
+ // Required parameters
+ name: 'crewaf001'
+ // Non-required parameters
+ capacity: 2
+ databases: [
+ {
+ clusteringPolicy: 'EnterpriseCluster'
+ evictionPolicy: 'AllKeysLFU'
+ modules: [
+ {
+ name: 'RedisBloom'
+ }
+ {
+ args: 'RETENTION_POLICY 20'
+ name: 'RedisTimeSeries'
+ }
+ ]
+ persistenceAofEnabled: true
+ persistenceAofFrequency: '1s'
+ persistenceRdbEnabled: false
+ port: 10000
+ }
+ ]
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "crewaf001"
+ },
+ // Non-required parameters
+ "capacity": {
+ "value": 2
+ },
+ "databases": {
+ "value": [
+ {
+ "clusteringPolicy": "EnterpriseCluster",
+ "evictionPolicy": "AllKeysLFU",
+ "modules": [
+ {
+ "name": "RedisBloom"
+ },
+ {
+ "args": "RETENTION_POLICY 20",
+ "name": "RedisTimeSeries"
+ }
+ ],
+ "persistenceAofEnabled": true,
+ "persistenceAofFrequency": "1s",
+ "persistenceRdbEnabled": false,
+ "port": 10000
+ }
+ ]
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "
+ ## Parameters diff --git a/modules/cache/redis-enterprise/tests/e2e/waf-aligned/dependencies.bicep b/modules/cache/redis-enterprise/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..59ae30a575 --- /dev/null +++ b/modules/cache/redis-enterprise/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,60 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.redisenterprise.cache.azure.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/cache/redis-enterprise/tests/e2e/waf-aligned/main.test.bicep b/modules/cache/redis-enterprise/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..cd0e90a7d9 --- /dev/null +++ b/modules/cache/redis-enterprise/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,135 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-cache.redisenterprise-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'crewaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-ds-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + capacity: 2 + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + minimumTlsVersion: '1.2' + zoneRedundant: true + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + databases: [ + { + clusteringPolicy: 'EnterpriseCluster' + evictionPolicy: 'AllKeysLFU' + modules: [ + { + name: 'RedisBloom' + } + { + name: 'RedisTimeSeries' + args: 'RETENTION_POLICY 20' + } + ] + persistenceAofEnabled: true + persistenceAofFrequency: '1s' + persistenceRdbEnabled: false + port: 10000 + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + resourceType: 'Redis Cache Enterprise' + } + } +} diff --git a/modules/cache/redis/README.md b/modules/cache/redis/README.md index 6c833b7a8a..500c93fa81 100644 --- a/modules/cache/redis/README.md +++ b/modules/cache/redis/README.md @@ -32,6 +32,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -263,6 +264,188 @@ module redis 'br:bicep/modules/cache.redis:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module redis 'br:bicep/modules/cache.redis:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-crwaf'
+ params: {
+ // Required parameters
+ name: 'crwaf001'
+ // Non-required parameters
+ capacity: 2
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "crwaf001"
+ },
+ // Non-required parameters
+ "capacity": {
+ "value": 2
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "
+ ## Parameters diff --git a/modules/cache/redis/tests/e2e/waf-aligned/dependencies.bicep b/modules/cache/redis/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..8218e0c1ad --- /dev/null +++ b/modules/cache/redis/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,60 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the managed identity to create.') +param managedIdentityName string + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.redis.cache.windows.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id diff --git a/modules/cache/redis/tests/e2e/waf-aligned/main.test.bicep b/modules/cache/redis/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..814b68ace3 --- /dev/null +++ b/modules/cache/redis/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,121 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-cache.redis-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'crwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + capacity: 2 + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + enableNonSslPort: true + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + minimumTlsVersion: '1.2' + zoneRedundant: true + zones: [ 1, 2 ] + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + publicNetworkAccess: 'Enabled' + redisVersion: '6' + shardCount: 1 + skuName: 'Premium' + managedIdentities: { + systemAssigned: true + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + tags: { + 'hidden-title': 'This is visible in the resource name' + resourceType: 'Redis Cache' + } + } +} diff --git a/modules/cdn/profile/README.md b/modules/cdn/profile/README.md index 41fd0159bf..47cbe6ed82 100644 --- a/modules/cdn/profile/README.md +++ b/modules/cdn/profile/README.md @@ -38,6 +38,7 @@ The following section provides usage examples for the module, which were used to - [Afd](#example-1-afd) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Afd_ @@ -404,6 +405,154 @@ module profile 'br:bicep/modules/cdn.profile:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module profile 'br:bicep/modules/cdn.profile:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-cdnpwaf'
+ params: {
+ // Required parameters
+ name: 'dep-test-cdnpwaf'
+ sku: 'Standard_Verizon'
+ // Non-required parameters
+ enableDefaultTelemetry: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "dep-test-cdnpwaf"
+ },
+ "sku": {
+ "value": "Standard_Verizon"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "
+ ## Parameters diff --git a/modules/cdn/profile/tests/e2e/waf-aligned/dependencies.bicep b/modules/cdn/profile/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..7ca387035b --- /dev/null +++ b/modules/cdn/profile/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,38 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: { + allowBlobPublicAccess: false + networkAcls: { + defaultAction: 'Deny' + bypass: 'AzureServices' + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Storage Account.') +output storageAccountResourceId string = storageAccount.id + +@description('The name of the created Storage Account.') +output storageAccountName string = storageAccount.name + +@description('The resource ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/cdn/profile/tests/e2e/waf-aligned/main.test.bicep b/modules/cdn/profile/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..8df82c8a93 --- /dev/null +++ b/modules/cdn/profile/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,101 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-cdn.profiles-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'cdnpwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + storageAccountName: 'dep${namePrefix}cdnstore${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + name: 'dep-${namePrefix}-test-${serviceShort}' + location: location + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + originResponseTimeoutSeconds: 60 + sku: 'Standard_Verizon' + enableDefaultTelemetry: enableDefaultTelemetry + endpointProperties: { + originHostHeader: '${nestedDependencies.outputs.storageAccountName}.blob.${environment().suffixes.storage}' + contentTypesToCompress: [ + 'text/plain' + 'text/html' + 'text/css' + 'text/javascript' + 'application/x-javascript' + 'application/javascript' + 'application/json' + 'application/xml' + ] + isCompressionEnabled: true + isHttpAllowed: true + isHttpsAllowed: true + queryStringCachingBehavior: 'IgnoreQueryString' + origins: [ + { + name: 'dep-${namePrefix}-cdn-endpoint01' + properties: { + hostName: '${nestedDependencies.outputs.storageAccountName}.blob.${environment().suffixes.storage}' + httpPort: 80 + httpsPort: 443 + enabled: true + } + } + ] + originGroups: [] + geoFilters: [] + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + } +} diff --git a/modules/cognitive-services/account/README.md b/modules/cognitive-services/account/README.md index 26626e96c2..4244365e44 100644 --- a/modules/cognitive-services/account/README.md +++ b/modules/cognitive-services/account/README.md @@ -36,6 +36,7 @@ The following section provides usage examples for the module, which were used to - [Encr](#example-2-encr) - [Using large parameter set](#example-3-using-large-parameter-set) - [Speech](#example-4-speech) +- [WAF-aligned](#example-5-waf-aligned) ### Example 1: _Using only defaults_ @@ -487,6 +488,206 @@ module account 'br:bicep/modules/cognitive-services.account:1.0.0' = {
+### Example 5: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module account 'br:bicep/modules/cognitive-services.account:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-csawaf'
+ params: {
+ // Required parameters
+ kind: 'Face'
+ name: 'csawaf001'
+ // Non-required parameters
+ customSubDomainName: 'xdomain'
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "kind": {
+ "value": "Face"
+ },
+ "name": {
+ "value": "csawaf001"
+ },
+ // Non-required parameters
+ "customSubDomainName": {
+ "value": "xdomain"
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "
+ ## Parameters diff --git a/modules/cognitive-services/account/tests/e2e/waf-aligned/dependencies.bicep b/modules/cognitive-services/account/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..129b6f6579 --- /dev/null +++ b/modules/cognitive-services/account/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,68 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + serviceEndpoints: [ + { + service: 'Microsoft.CognitiveServices' + } + ] + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.cognitiveservices.azure.com' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Private DNS zone.') +output privateDNSZoneResourceId string = privateDNSZone.id diff --git a/modules/cognitive-services/account/tests/e2e/waf-aligned/main.test.bicep b/modules/cognitive-services/account/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..6db604335b --- /dev/null +++ b/modules/cognitive-services/account/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,137 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-cognitiveservices.accounts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'csawaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + kind: 'Face' + customSubDomainName: '${namePrefix}xdomain' + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + networkAcls: { + defaultAction: 'Deny' + ipRules: [ + { + value: '40.74.28.0/23' + } + ] + virtualNetworkRules: [ + { + id: nestedDependencies.outputs.subnetResourceId + ignoreMissingVnetServiceEndpoint: false + } + ] + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + sku: 'S0' + managedIdentities: { + systemAssigned: true + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/compute/availability-set/README.md b/modules/compute/availability-set/README.md index e2d646e9bf..b78be7385e 100644 --- a/modules/compute/availability-set/README.md +++ b/modules/compute/availability-set/README.md @@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -167,6 +168,96 @@ module availabilitySet 'br:bicep/modules/compute.availability-set:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module availabilitySet 'br:bicep/modules/compute.availability-set:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-caswaf'
+ params: {
+ // Required parameters
+ name: 'caswaf001'
+ // Non-required parameters
+ enableDefaultTelemetry: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "caswaf001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "
+ ## Parameters diff --git a/modules/compute/availability-set/tests/e2e/waf-aligned/dependencies.bicep b/modules/compute/availability-set/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..2c78999e90 --- /dev/null +++ b/modules/compute/availability-set/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,24 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Proximity Placement Group to create.') +param proximityPlacementGroupName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource proximityPlacementGroup 'Microsoft.Compute/proximityPlacementGroups@2022-03-01' = { + name: proximityPlacementGroupName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Proximity Placement Group.') +output proximityPlacementGroupResourceId string = proximityPlacementGroup.id diff --git a/modules/compute/availability-set/tests/e2e/waf-aligned/main.test.bicep b/modules/compute/availability-set/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..01bac9f002 --- /dev/null +++ b/modules/compute/availability-set/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,74 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-compute.availabilitysets-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'caswaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + proximityPlacementGroupName: 'dep-${namePrefix}-ppg-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + proximityPlacementGroupResourceId: nestedDependencies.outputs.proximityPlacementGroupResourceId + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/compute/disk-encryption-set/README.md b/modules/compute/disk-encryption-set/README.md index c3d9e9d920..b9590d9b21 100644 --- a/modules/compute/disk-encryption-set/README.md +++ b/modules/compute/disk-encryption-set/README.md @@ -30,6 +30,7 @@ The following section provides usage examples for the module, which were used to - [Accesspolicies](#example-1-accesspolicies) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Accesspolicies_ @@ -232,6 +233,112 @@ module diskEncryptionSet 'br:bicep/modules/compute.disk-encryption-set:1.0.0' =
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module diskEncryptionSet 'br:bicep/modules/compute.disk-encryption-set:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-cdeswaf'
+ params: {
+ // Required parameters
+ keyName: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "keyName": {
+ "value": "
+ ## Parameters diff --git a/modules/compute/disk-encryption-set/tests/e2e/waf-aligned/dependencies.bicep b/modules/compute/disk-encryption-set/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..62321ebe98 --- /dev/null +++ b/modules/compute/disk-encryption-set/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,51 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: true // Required by disk encryption set + softDeleteRetentionInDays: 7 + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + + resource key 'keys@2022-07-01' = { + name: 'keyEncryptionKey' + properties: { + kty: 'RSA' + } + } +} + +@description('The resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id + +@description('The name of the created encryption key.') +output keyName string = keyVault::key.name + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id diff --git a/modules/compute/disk-encryption-set/tests/e2e/waf-aligned/main.test.bicep b/modules/compute/disk-encryption-set/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..e5354c3489 --- /dev/null +++ b/modules/compute/disk-encryption-set/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,84 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-compute.diskencryptionsets-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'cdeswaf' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total) + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + keyName: nestedDependencies.outputs.keyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + managedIdentities: { + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/compute/disk/README.md b/modules/compute/disk/README.md index 53656e6a71..a2b245fd26 100644 --- a/modules/compute/disk/README.md +++ b/modules/compute/disk/README.md @@ -30,6 +30,7 @@ The following section provides usage examples for the module, which were used to - [Image](#example-2-image) - [Import](#example-3-import) - [Using large parameter set](#example-4-using-large-parameter-set) +- [WAF-aligned](#example-5-waf-aligned) ### Example 1: _Using only defaults_ @@ -375,6 +376,120 @@ module disk 'br:bicep/modules/compute.disk:1.0.0' = {
+### Example 5: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module disk 'br:bicep/modules/compute.disk:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-cdwaf'
+ params: {
+ // Required parameters
+ name: 'cdwaf001'
+ sku: 'UltraSSD_LRS'
+ // Non-required parameters
+ diskIOPSReadWrite: 500
+ diskMBpsReadWrite: 60
+ diskSizeGB: 128
+ enableDefaultTelemetry: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "cdwaf001"
+ },
+ "sku": {
+ "value": "UltraSSD_LRS"
+ },
+ // Non-required parameters
+ "diskIOPSReadWrite": {
+ "value": 500
+ },
+ "diskMBpsReadWrite": {
+ "value": 60
+ },
+ "diskSizeGB": {
+ "value": 128
+ },
+ "enableDefaultTelemetry": {
+ "value": "
+ ## Parameters diff --git a/modules/compute/disk/tests/e2e/waf-aligned/dependencies.bicep b/modules/compute/disk/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..616cf219fe --- /dev/null +++ b/modules/compute/disk/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created managed identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/compute/disk/tests/e2e/waf-aligned/main.test.bicep b/modules/compute/disk/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..95bd0f5d73 --- /dev/null +++ b/modules/compute/disk/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,78 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-compute.images-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'cdwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}-${serviceShort}001' + sku: 'UltraSSD_LRS' + diskIOPSReadWrite: 500 + diskMBpsReadWrite: 60 + diskSizeGB: 128 + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + logicalSectorSize: 512 + osType: 'Windows' + publicNetworkAccess: 'Enabled' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/compute/gallery/README.md b/modules/compute/gallery/README.md index 4f370dfd3b..5d352f0fb3 100644 --- a/modules/compute/gallery/README.md +++ b/modules/compute/gallery/README.md @@ -30,6 +30,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -403,6 +404,330 @@ module gallery 'br:bicep/modules/compute.gallery:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module gallery 'br:bicep/modules/compute.gallery:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-cgwaf'
+ params: {
+ // Required parameters
+ name: 'cgwaf001'
+ // Non-required parameters
+ applications: [
+ {
+ name: 'cgwaf-appd-001'
+ }
+ {
+ name: 'cgwaf-appd-002'
+ roleAssignments: [
+ {
+ principalId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "cgwaf001"
+ },
+ // Non-required parameters
+ "applications": {
+ "value": [
+ {
+ "name": "cgwaf-appd-001"
+ },
+ {
+ "name": "cgwaf-appd-002",
+ "roleAssignments": [
+ {
+ "principalId": "
+ ## Parameters diff --git a/modules/compute/gallery/tests/e2e/waf-aligned/dependencies.bicep b/modules/compute/gallery/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..a7f42aee7b --- /dev/null +++ b/modules/compute/gallery/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/compute/gallery/tests/e2e/waf-aligned/main.test.bicep b/modules/compute/gallery/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..755e9e49c5 --- /dev/null +++ b/modules/compute/gallery/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,189 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-compute.galleries-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'cgwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + applications: [ + { + name: '${namePrefix}-${serviceShort}-appd-001' + } + { + name: '${namePrefix}-${serviceShort}-appd-002' + supportedOSType: 'Windows' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + } + ] + images: [ + { + name: '${namePrefix}-az-imgd-ws-001' + } + { + hyperVGeneration: 'V1' + maxRecommendedMemory: 16 + maxRecommendedvCPUs: 8 + minRecommendedMemory: 4 + minRecommendedvCPUs: 2 + name: '${namePrefix}-az-imgd-ws-002' + offer: 'WindowsServer' + osState: 'Generalized' + osType: 'Windows' + publisher: 'MicrosoftWindowsServer' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + sku: '2022-datacenter-azure-edition' + } + { + hyperVGeneration: 'V2' + isHibernateSupported: 'true' + maxRecommendedMemory: 16 + maxRecommendedvCPUs: 8 + minRecommendedMemory: 4 + minRecommendedvCPUs: 2 + name: '${namePrefix}-az-imgd-ws-003' + offer: 'WindowsServer' + osState: 'Generalized' + osType: 'Windows' + publisher: 'MicrosoftWindowsServer' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + sku: '2022-datacenter-azure-edition-hibernate' + } + { + hyperVGeneration: 'V2' + isAcceleratedNetworkSupported: 'true' + maxRecommendedMemory: 16 + maxRecommendedvCPUs: 8 + minRecommendedMemory: 4 + minRecommendedvCPUs: 2 + name: '${namePrefix}-az-imgd-ws-004' + offer: 'WindowsServer' + osState: 'Generalized' + osType: 'Windows' + publisher: 'MicrosoftWindowsServer' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + sku: '2022-datacenter-azure-edition-accnet' + } + { + hyperVGeneration: 'V2' + securityType: 'TrustedLaunch' + maxRecommendedMemory: 16 + maxRecommendedvCPUs: 4 + minRecommendedMemory: 4 + minRecommendedvCPUs: 2 + name: '${namePrefix}-az-imgd-wdtl-002' + offer: 'WindowsDesktop' + osState: 'Generalized' + osType: 'Windows' + publisher: 'MicrosoftWindowsDesktop' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + sku: 'Win11-21H2' + } + { + hyperVGeneration: 'V2' + maxRecommendedMemory: 32 + maxRecommendedvCPUs: 4 + minRecommendedMemory: 4 + minRecommendedvCPUs: 1 + name: '${namePrefix}-az-imgd-us-001' + offer: '0001-com-ubuntu-server-focal' + osState: 'Generalized' + osType: 'Linux' + publisher: 'canonical' + sku: '20_04-lts-gen2' + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/compute/image/README.md b/modules/compute/image/README.md index 6c22d0ff2d..f642c6f3c1 100644 --- a/modules/compute/image/README.md +++ b/modules/compute/image/README.md @@ -26,6 +26,7 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br:bicep/modules/compute.image:1.0.0`. - [Using large parameter set](#example-1-using-large-parameter-set) +- [WAF-aligned](#example-2-waf-aligned) ### Example 1: _Using large parameter set_ @@ -139,6 +140,118 @@ module image 'br:bicep/modules/compute.image:1.0.0' = {
+### Example 2: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module image 'br:bicep/modules/compute.image:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-ciwaf'
+ params: {
+ // Required parameters
+ name: 'ciwaf001'
+ osAccountType: 'Premium_LRS'
+ osDiskBlobUri: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "ciwaf001"
+ },
+ "osAccountType": {
+ "value": "Premium_LRS"
+ },
+ "osDiskBlobUri": {
+ "value": "
+ ## Parameters diff --git a/modules/compute/image/tests/e2e/waf-aligned/dependencies.bicep b/modules/compute/image/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..2a31d8730b --- /dev/null +++ b/modules/compute/image/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,218 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Storage Account to create and to copy the VHD into.') +param storageAccountName string + +@description('Required. The name of the Disk Encryption Set to create.') +param diskEncryptionSetName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The name prefix of the Image Template to create.') +param imageTemplateNamePrefix string + +@description('Generated. Do not provide a value! This date value is used to generate a unique image template name.') +param baseTime string = utcNow('yyyy-MM-dd-HH-mm-ss') + +@description('Required. The name of the Deployment Script to create for triggering the image creation.') +param triggerImageDeploymentScriptName string + +@description('Required. The name of the Deployment Script to copy the VHD to a destination storage account.') +param copyVhdDeploymentScriptName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = { + name: storageAccountName + location: location + kind: 'StorageV2' + sku: { + name: 'Standard_LRS' + } + properties: { + allowBlobPublicAccess: false + } + resource blobServices 'blobServices@2022-09-01' = { + name: 'default' + resource container 'containers@2022-09-01' = { + name: 'vhds' + properties: { + publicAccess: 'None' + } + } + } +} + +module roleAssignment 'dependencies_rbac.bicep' = { + name: '${deployment().name}-MSI-roleAssignment' + scope: subscription() + params: { + managedIdentityPrincipalId: managedIdentity.properties.principalId + managedIdentityResourceId: managedIdentity.id + } +} + +// Deploy image template +resource imageTemplate 'Microsoft.VirtualMachineImages/imageTemplates@2022-02-14' = { + #disable-next-line use-stable-resource-identifiers + name: '${imageTemplateNamePrefix}-${baseTime}' + location: location + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + buildTimeoutInMinutes: 0 + vmProfile: { + vmSize: 'Standard_D2s_v3' + osDiskSizeGB: 127 + } + source: { + type: 'PlatformImage' + publisher: 'MicrosoftWindowsDesktop' + offer: 'Windows-11' + sku: 'win11-21h2-avd' + version: 'latest' + } + distribute: [ + { + type: 'VHD' + runOutputName: '${imageTemplateNamePrefix}-VHD' + artifactTags: {} + } + ] + customize: [ + { + restartTimeout: '30m' + type: 'WindowsRestart' + } + ] + } +} + +// Trigger VHD creation +resource triggerImageDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: triggerImageDeploymentScriptName + location: location + kind: 'AzurePowerShell' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + azPowerShellVersion: '8.0' + retentionInterval: 'P1D' + arguments: '-ImageTemplateName \\"${imageTemplate.name}\\" -ImageTemplateResourceGroup \\"${resourceGroup().name}\\"' + scriptContent: loadTextContent('../../../../../.shared/.scripts/Start-ImageTemplate.ps1') + cleanupPreference: 'OnSuccess' + forceUpdateTag: baseTime + } + dependsOn: [ + roleAssignment + ] +} + +// Copy VHD to destination storage account +resource copyVhdDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: copyVhdDeploymentScriptName + location: location + kind: 'AzurePowerShell' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + azPowerShellVersion: '8.0' + retentionInterval: 'P1D' + arguments: '-ImageTemplateName \\"${imageTemplate.name}\\" -ImageTemplateResourceGroup \\"${resourceGroup().name}\\" -DestinationStorageAccountName \\"${storageAccount.name}\\" -VhdName \\"${imageTemplateNamePrefix}\\" -WaitForComplete' + scriptContent: loadTextContent('../../../../../.shared/.scripts/Copy-VhdToStorageAccount.ps1') + cleanupPreference: 'OnSuccess' + forceUpdateTag: baseTime + } + dependsOn: [ triggerImageDeploymentScript ] +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: true // Required for encrption to work + softDeleteRetentionInDays: 7 + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + + resource key 'keys@2022-07-01' = { + name: 'encryptionKey' + properties: { + kty: 'RSA' + } + } +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${keyVault::key.id}-${location}-${managedIdentity.id}-Key-Reader-RoleAssignment') + scope: keyVault::key + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') // Key Vault Crypto User + principalType: 'ServicePrincipal' + } +} + +resource diskEncryptionSet 'Microsoft.Compute/diskEncryptionSets@2022-07-02' = { + name: diskEncryptionSetName + location: location + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + activeKey: { + sourceVault: { + id: keyVault.id + } + keyUrl: keyVault::key.properties.keyUriWithVersion + } + encryptionType: 'EncryptionAtRestWithCustomerKey' + } + dependsOn: [ + keyPermissions + ] +} + +@description('The URI of the created VHD.') +output vhdUri string = 'https://${storageAccount.name}.blob.${environment().suffixes.storage}/vhds/${imageTemplateNamePrefix}.vhd' + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The resource ID of the created Disk Encryption Set.') +output diskEncryptionSetResourceId string = diskEncryptionSet.id diff --git a/modules/compute/image/tests/e2e/waf-aligned/dependencies_rbac.bicep b/modules/compute/image/tests/e2e/waf-aligned/dependencies_rbac.bicep new file mode 100644 index 0000000000..cdca1b63bd --- /dev/null +++ b/modules/compute/image/tests/e2e/waf-aligned/dependencies_rbac.bicep @@ -0,0 +1,16 @@ +targetScope = 'subscription' + +@description('Required. The resource ID of the created Managed Identity.') +param managedIdentityResourceId string + +@description('Required. The principal ID of the created Managed Identity.') +param managedIdentityPrincipalId string + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().subscriptionId, 'Contributor', managedIdentityResourceId) + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') // Contributor + principalId: managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } +} diff --git a/modules/compute/image/tests/e2e/waf-aligned/main.test.bicep b/modules/compute/image/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..83e55ae5ed --- /dev/null +++ b/modules/compute/image/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,86 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-compute.images-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'ciwaf' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total) + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' + diskEncryptionSetName: 'dep-${namePrefix}-des-${serviceShort}' + storageAccountName: 'dep${namePrefix}sa${serviceShort}01' + imageTemplateNamePrefix: 'dep-${namePrefix}-imgt-${serviceShort}' + triggerImageDeploymentScriptName: 'dep-${namePrefix}-ds-${serviceShort}-triggerImageTemplate' + copyVhdDeploymentScriptName: 'dep-${namePrefix}-ds-${serviceShort}-copyVhdToStorage' + } +} + +// ============== // +// Test Execution // +// ============== // +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + osAccountType: 'Premium_LRS' + osDiskBlobUri: nestedDependencies.outputs.vhdUri + osDiskCaching: 'ReadWrite' + osType: 'Windows' + hyperVGeneration: 'V1' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + zoneResilient: true + diskEncryptionSetResourceId: nestedDependencies.outputs.diskEncryptionSetResourceId + osState: 'Generalized' + diskSizeGB: 128 + tags: { + 'hidden-title': 'This is visible in the resource name' + tagA: 'You\'re it' + tagB: 'Player' + } + } +} diff --git a/modules/compute/proximity-placement-group/README.md b/modules/compute/proximity-placement-group/README.md index 821a6a502e..a5861c05f9 100644 --- a/modules/compute/proximity-placement-group/README.md +++ b/modules/compute/proximity-placement-group/README.md @@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -203,6 +204,132 @@ module proximityPlacementGroup 'br:bicep/modules/compute.proximity-placement-gro
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module proximityPlacementGroup 'br:bicep/modules/compute.proximity-placement-group:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-cppgwaf'
+ params: {
+ // Required parameters
+ name: 'cppgwaf001'
+ // Non-required parameters
+ colocationStatus: {
+ code: 'ColocationStatus/Aligned'
+ displayStatus: 'Aligned'
+ level: 'Info'
+ message: 'I\'m a default error message'
+ }
+ enableDefaultTelemetry: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "cppgwaf001"
+ },
+ // Non-required parameters
+ "colocationStatus": {
+ "value": {
+ "code": "ColocationStatus/Aligned",
+ "displayStatus": "Aligned",
+ "level": "Info",
+ "message": "I\"m a default error message"
+ }
+ },
+ "enableDefaultTelemetry": {
+ "value": "
+ ## Parameters diff --git a/modules/compute/proximity-placement-group/tests/e2e/waf-aligned/dependencies.bicep b/modules/compute/proximity-placement-group/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..a7f42aee7b --- /dev/null +++ b/modules/compute/proximity-placement-group/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/compute/proximity-placement-group/tests/e2e/waf-aligned/main.test.bicep b/modules/compute/proximity-placement-group/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..d58853a01e --- /dev/null +++ b/modules/compute/proximity-placement-group/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,88 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-compute.proximityplacementgroups-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'cppgwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + zones: [ + '1' + ] + type: 'Standard' + tags: { + 'hidden-title': 'This is visible in the resource name' + TagA: 'Would you kindly...' + TagB: 'Tags for sale' + } + colocationStatus: { + code: 'ColocationStatus/Aligned' + displayStatus: 'Aligned' + level: 'Info' + message: 'I\'m a default error message' + } + intent: { + vmSizes: [ + 'Standard_B1ms' + 'Standard_B4ms' + ] + } + } +} diff --git a/modules/compute/ssh-public-key/README.md b/modules/compute/ssh-public-key/README.md index fcc48b1abe..096bdf0a7f 100644 --- a/modules/compute/ssh-public-key/README.md +++ b/modules/compute/ssh-public-key/README.md @@ -32,6 +32,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -133,6 +134,58 @@ module sshPublicKey 'br:bicep/modules/compute.ssh-public-key:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module sshPublicKey 'br:bicep/modules/compute.ssh-public-key:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-cspkwaf'
+ params: {
+ // Required parameters
+ name: 'sshkey-cspkwaf001'
+ // Non-required parameters
+ enableDefaultTelemetry: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "sshkey-cspkwaf001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "
+ ## Parameters diff --git a/modules/compute/ssh-public-key/tests/e2e/waf-aligned/dependencies.bicep b/modules/compute/ssh-public-key/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..13a584595b --- /dev/null +++ b/modules/compute/ssh-public-key/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,61 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Optional. Name of the Deployment Script that creates the SSH Public Key.') +param generateSshPubKeyScriptName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. Name of the temporary SSH Public Key to create for test.') +param sshKeyName string + +@description('Optional. Do not provide a value. Used to force the deployment script to rerun on every redeployment.') +param utcValue string = utcNow() + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +// required for the deployment script to create a new temporary ssh public key object +resource msi_ContributorRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(resourceGroup().id, 'ManagedIdentityContributor', '[[namePrefix]]') + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') // Contributor + principalId: managedIdentity.properties.principalId + principalType: 'ServicePrincipal' + } +} + +resource createPubKeyScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: generateSshPubKeyScriptName + location: location + kind: 'AzurePowerShell' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + azPowerShellVersion: '8.0' + retentionInterval: 'P1D' + arguments: '-ResourceGroupName ${resourceGroup().name} -SSHKeyName ${sshKeyName}' + scriptContent: loadTextContent('../../../../../.shared/.scripts/New-SSHKey.ps1') + cleanupPreference: 'OnExpiration' + forceUpdateTag: utcValue + } + dependsOn: [ + msi_ContributorRoleAssignment + ] +} + +@description('The public key to be added to the SSH Public Key resource.') +output publicKey string = createPubKeyScript.properties.outputs.publicKey + +@description('The resource ID of the managed Identity') +output managedIdentityId string = managedIdentity.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/compute/ssh-public-key/tests/e2e/waf-aligned/main.test.bicep b/modules/compute/ssh-public-key/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..e432ba94de --- /dev/null +++ b/modules/compute/ssh-public-key/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,60 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-compute.sshPublicKeys-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +@maxLength(7) +param serviceShort string = 'cspkwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + generateSshPubKeyScriptName: 'dep-${namePrefix}-ds-${serviceShort}-generateSshPubKey' + sshKeyName: 'dep-${namePrefix}-ssh-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}-sshkey-${serviceShort}001' + publicKey: nestedDependencies.outputs.publicKey + } +} diff --git a/modules/consumption/budget/README.md b/modules/consumption/budget/README.md index 44cad18b76..748abdf07f 100644 --- a/modules/consumption/budget/README.md +++ b/modules/consumption/budget/README.md @@ -26,6 +26,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -163,6 +164,82 @@ module budget 'br:bicep/modules/consumption.budget:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module budget 'br:bicep/modules/consumption.budget:1.0.0' = {
+ name: '${uniqueString(deployment().name)}-test-cbwaf'
+ params: {
+ // Required parameters
+ amount: 500
+ name: 'cbwaf001'
+ // Non-required parameters
+ contactEmails: [
+ 'dummy@contoso.com'
+ ]
+ enableDefaultTelemetry: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "amount": {
+ "value": 500
+ },
+ "name": {
+ "value": "cbwaf001"
+ },
+ // Non-required parameters
+ "contactEmails": {
+ "value": [
+ "dummy@contoso.com"
+ ]
+ },
+ "enableDefaultTelemetry": {
+ "value": "
+ ## Parameters diff --git a/modules/consumption/budget/tests/e2e/waf-aligned/main.test.bicep b/modules/consumption/budget/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..ec51e97926 --- /dev/null +++ b/modules/consumption/budget/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,40 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'cbwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + amount: 500 + contactEmails: [ + 'dummy@contoso.com' + ] + thresholds: [ + 50 + 75 + 90 + 100 + 110 + ] + } +} diff --git a/modules/container-instance/container-group/README.md b/modules/container-instance/container-group/README.md index 7918b1c8a2..447234e1d2 100644 --- a/modules/container-instance/container-group/README.md +++ b/modules/container-instance/container-group/README.md @@ -30,6 +30,7 @@ The following section provides usage examples for the module, which were used to - [Encr](#example-2-encr) - [Using large parameter set](#example-3-using-large-parameter-set) - [Private](#example-4-private) +- [WAF-aligned](#example-5-waf-aligned) ### Example 1: _Using only defaults_ @@ -783,6 +784,206 @@ module containerGroup 'br:bicep/modules/container-instance.container-group:1.0.0
+### Example 5: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module containerGroup 'br:bicep/modules/container-instance.container-group:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-cicgwaf'
+ params: {
+ // Required parameters
+ containers: [
+ {
+ name: 'az-aci-x-001'
+ properties: {
+ command: []
+ environmentVariables: []
+ image: 'mcr.microsoft.com/azuredocs/aci-helloworld'
+ ports: [
+ {
+ port: '80'
+ protocol: 'Tcp'
+ }
+ {
+ port: '443'
+ protocol: 'Tcp'
+ }
+ ]
+ resources: {
+ requests: {
+ cpu: 2
+ memoryInGB: 2
+ }
+ }
+ }
+ }
+ {
+ name: 'az-aci-x-002'
+ properties: {
+ command: []
+ environmentVariables: []
+ image: 'mcr.microsoft.com/azuredocs/aci-helloworld'
+ ports: [
+ {
+ port: '8080'
+ protocol: 'Tcp'
+ }
+ ]
+ resources: {
+ requests: {
+ cpu: 2
+ memoryInGB: 2
+ }
+ }
+ }
+ }
+ ]
+ name: 'cicgwaf001'
+ // Non-required parameters
+ enableDefaultTelemetry: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "containers": {
+ "value": [
+ {
+ "name": "az-aci-x-001",
+ "properties": {
+ "command": [],
+ "environmentVariables": [],
+ "image": "mcr.microsoft.com/azuredocs/aci-helloworld",
+ "ports": [
+ {
+ "port": "80",
+ "protocol": "Tcp"
+ },
+ {
+ "port": "443",
+ "protocol": "Tcp"
+ }
+ ],
+ "resources": {
+ "requests": {
+ "cpu": 2,
+ "memoryInGB": 2
+ }
+ }
+ }
+ },
+ {
+ "name": "az-aci-x-002",
+ "properties": {
+ "command": [],
+ "environmentVariables": [],
+ "image": "mcr.microsoft.com/azuredocs/aci-helloworld",
+ "ports": [
+ {
+ "port": "8080",
+ "protocol": "Tcp"
+ }
+ ],
+ "resources": {
+ "requests": {
+ "cpu": 2,
+ "memoryInGB": 2
+ }
+ }
+ }
+ }
+ ]
+ },
+ "name": {
+ "value": "cicgwaf001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "
+ ## Parameters diff --git a/modules/container-instance/container-group/tests/e2e/waf-aligned/dependencies.bicep b/modules/container-instance/container-group/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..66dc10c2f2 --- /dev/null +++ b/modules/container-instance/container-group/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Required. The name of the managed identity to create.') +param managedIdentityName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created managed identity.') +output managedIdentityResourceId string = managedIdentity.id diff --git a/modules/container-instance/container-group/tests/e2e/waf-aligned/main.test.bicep b/modules/container-instance/container-group/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..389ed3cfc7 --- /dev/null +++ b/modules/container-instance/container-group/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,127 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-containerinstance.containergroups-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'cicgwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + containers: [ + { + name: '${namePrefix}-az-aci-x-001' + properties: { + command: [] + environmentVariables: [] + image: 'mcr.microsoft.com/azuredocs/aci-helloworld' + ports: [ + { + port: '80' + protocol: 'Tcp' + } + { + port: '443' + protocol: 'Tcp' + } + ] + resources: { + requests: { + cpu: 2 + memoryInGB: 2 + } + } + } + } + { + name: '${namePrefix}-az-aci-x-002' + properties: { + command: [] + environmentVariables: [] + image: 'mcr.microsoft.com/azuredocs/aci-helloworld' + ports: [ + { + port: '8080' + protocol: 'Tcp' + } + ] + resources: { + requests: { + cpu: 2 + memoryInGB: 2 + } + } + } + } + ] + ipAddressPorts: [ + { + protocol: 'Tcp' + port: 80 + } + { + protocol: 'Tcp' + port: 443 + } + ] + managedIdentities: { + systemAssigned: true + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/container-registry/registry/README.md b/modules/container-registry/registry/README.md index 940cac8fae..ecb4f44dc9 100644 --- a/modules/container-registry/registry/README.md +++ b/modules/container-registry/registry/README.md @@ -36,6 +36,7 @@ The following section provides usage examples for the module, which were used to - [Encr](#example-2-encr) - [Using large parameter set](#example-3-using-large-parameter-set) - [Pe](#example-4-pe) +- [WAF-aligned](#example-5-waf-aligned) ### Example 1: _Using only defaults_ @@ -519,6 +520,262 @@ module registry 'br:bicep/modules/container-registry.registry:1.0.0' = {
+### Example 5: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module registry 'br:bicep/modules/container-registry.registry:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-crrwaf'
+ params: {
+ // Required parameters
+ name: 'crrwaf001'
+ // Non-required parameters
+ acrAdminUserEnabled: false
+ acrSku: 'Premium'
+ azureADAuthenticationAsArmPolicyStatus: 'enabled'
+ cacheRules: [
+ {
+ name: 'customRule'
+ sourceRepository: 'docker.io/library/hello-world'
+ targetRepository: 'cached-docker-hub/hello-world'
+ }
+ {
+ sourceRepository: 'docker.io/library/hello-world'
+ }
+ ]
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "crrwaf001"
+ },
+ // Non-required parameters
+ "acrAdminUserEnabled": {
+ "value": false
+ },
+ "acrSku": {
+ "value": "Premium"
+ },
+ "azureADAuthenticationAsArmPolicyStatus": {
+ "value": "enabled"
+ },
+ "cacheRules": {
+ "value": [
+ {
+ "name": "customRule",
+ "sourceRepository": "docker.io/library/hello-world",
+ "targetRepository": "cached-docker-hub/hello-world"
+ },
+ {
+ "sourceRepository": "docker.io/library/hello-world"
+ }
+ ]
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "
+ ## Parameters diff --git a/modules/container-registry/registry/tests/e2e/waf-aligned/dependencies.bicep b/modules/container-registry/registry/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..4e89a810a0 --- /dev/null +++ b/modules/container-registry/registry/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,99 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Deployment Script to create to get the paired region name.') +param pairedRegionScriptName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink${environment().suffixes.acrLoginServer}' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${location}-${managedIdentity.id}-Reader-RoleAssignment') + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') // Reader + principalType: 'ServicePrincipal' + } +} + +resource getPairedRegionScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: pairedRegionScriptName + location: location + kind: 'AzurePowerShell' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + azPowerShellVersion: '8.0' + retentionInterval: 'P1D' + arguments: '-Location \\"${location}\\"' + scriptContent: loadTextContent('../../../../../.shared/.scripts/Get-PairedRegion.ps1') + } + dependsOn: [ + roleAssignment + ] +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id + +@description('The name of the paired region.') +output pairedRegionName string = getPairedRegionScript.properties.outputs.pairedRegionName diff --git a/modules/container-registry/registry/tests/e2e/waf-aligned/main.test.bicep b/modules/container-registry/registry/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..c2373864c7 --- /dev/null +++ b/modules/container-registry/registry/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,160 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-containerregistry.registries-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'crrwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total) + location: location + managedIdentityName: 'dep-${namePrefix}-msi-ds-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + pairedRegionScriptName: 'dep-${namePrefix}-ds-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + acrAdminUserEnabled: false + acrSku: 'Premium' + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + exportPolicyStatus: 'enabled' + azureADAuthenticationAsArmPolicyStatus: 'enabled' + softDeletePolicyStatus: 'disabled' + softDeletePolicyDays: 7 + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + privateEndpoints: [ + { + service: 'registry' + subnetResourceId: nestedDependencies.outputs.subnetResourceId + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + networkRuleSetIpRules: [ + { + action: 'Allow' + value: '40.74.28.0/23' + } + ] + quarantinePolicyStatus: 'enabled' + replications: [ + { + location: nestedDependencies.outputs.pairedRegionName + name: nestedDependencies.outputs.pairedRegionName + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + managedIdentities: { + systemAssigned: true + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + trustPolicyStatus: 'enabled' + cacheRules: [ + { + name: 'customRule' + sourceRepository: 'docker.io/library/hello-world' + targetRepository: 'cached-docker-hub/hello-world' + } + { + sourceRepository: 'docker.io/library/hello-world' + } + ] + webhooks: [ + { + name: '${namePrefix}acrx001webhook' + serviceUri: 'https://www.contoso.com/webhook' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/data-factory/factory/README.md b/modules/data-factory/factory/README.md index 4df25ff5d9..371644a9d8 100644 --- a/modules/data-factory/factory/README.md +++ b/modules/data-factory/factory/README.md @@ -35,6 +35,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -330,6 +331,252 @@ module factory 'br:bicep/modules/data-factory.factory:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module factory 'br:bicep/modules/data-factory.factory:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-dffwaf'
+ params: {
+ // Required parameters
+ name: 'dffwaf001'
+ // Non-required parameters
+ customerManagedKey: {
+ keyName: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "dffwaf001"
+ },
+ // Non-required parameters
+ "customerManagedKey": {
+ "value": {
+ "keyName": "
+ ## Parameters diff --git a/modules/data-factory/factory/tests/e2e/waf-aligned/dependencies.bicep b/modules/data-factory/factory/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..a6ab43ad7a --- /dev/null +++ b/modules/data-factory/factory/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,135 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.datafactory.azure.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetworkName}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: null + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + + resource key 'keys@2022-07-01' = { + name: 'encryptionKey' + properties: { + kty: 'RSA' + } + } +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${keyVault::key.id}-${location}-${managedIdentity.id}-KeyVault-Key-Read-RoleAssignment') + scope: keyVault::key + properties: { + principalId: managedIdentity.properties.principalId + // Key Vault Crypto User + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') + principalType: 'ServicePrincipal' + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = { + name: storageAccountName + location: location + kind: 'StorageV2' + sku: { + name: 'Standard_LRS' + } + properties: { + allowBlobPublicAccess: false + } +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id + +@description('The resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id + +@description('The URL of the created Key Vault.') +output keyVaultUrl string = keyVault.properties.vaultUri + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The name of the created Key Vault Encryption Key.') +output keyVaultEncryptionKeyName string = keyVault::key.name + +@description('The resource ID of the created Storage Account.') +output storageAccountResourceId string = storageAccount.id + +@description('The name of the created Storage Account.') +output storageAccountName string = storageAccount.name + +@description('The Blob Endpoint of the created Storage Account.') +output storageAccountBlobEndpoint string = storageAccount.properties.primaryEndpoints.blob diff --git a/modules/data-factory/factory/tests/e2e/waf-aligned/main.test.bicep b/modules/data-factory/factory/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..8c332672b1 --- /dev/null +++ b/modules/data-factory/factory/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,161 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-datafactory.factories-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'dffwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + storageAccountName: 'dep${namePrefix}st${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + customerManagedKey: { + keyName: nestedDependencies.outputs.keyVaultEncryptionKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + } + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + gitConfigureLater: true + globalParameters: { + testParameter1: { + type: 'String' + value: 'testValue1' + } + } + integrationRuntimes: [ + { + managedVirtualNetworkName: 'default' + name: 'AutoResolveIntegrationRuntime' + type: 'Managed' + typeProperties: { + computeProperties: { + location: 'AutoResolve' + } + } + } + + { + name: 'TestRuntime' + type: 'SelfHosted' + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + managedPrivateEndpoints: [ + { + fqdns: [ + nestedDependencies.outputs.storageAccountBlobEndpoint + ] + groupId: 'blob' + name: '${nestedDependencies.outputs.storageAccountName}-managed-privateEndpoint' + privateLinkResourceId: nestedDependencies.outputs.storageAccountResourceId + } + ] + managedVirtualNetworkName: 'default' + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + application: 'CARML' + } + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + managedIdentities: { + systemAssigned: true + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/data-protection/backup-vault/README.md b/modules/data-protection/backup-vault/README.md index 200b51d6bc..a7771b8b43 100644 --- a/modules/data-protection/backup-vault/README.md +++ b/modules/data-protection/backup-vault/README.md @@ -30,6 +30,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -303,6 +304,230 @@ module backupVault 'br:bicep/modules/data-protection.backup-vault:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module backupVault 'br:bicep/modules/data-protection.backup-vault:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-dpbvwaf'
+ params: {
+ // Required parameters
+ name: 'dpbvwaf001'
+ // Non-required parameters
+ azureMonitorAlertSettingsAlertsForAllJobFailures: 'Disabled'
+ backupPolicies: [
+ {
+ name: 'DefaultPolicy'
+ properties: {
+ datasourceTypes: [
+ 'Microsoft.Compute/disks'
+ ]
+ objectType: 'BackupPolicy'
+ policyRules: [
+ {
+ backupParameters: {
+ backupType: 'Incremental'
+ objectType: 'AzureBackupParams'
+ }
+ dataStore: {
+ dataStoreType: 'OperationalStore'
+ objectType: 'DataStoreInfoBase'
+ }
+ name: 'BackupDaily'
+ objectType: 'AzureBackupRule'
+ trigger: {
+ objectType: 'ScheduleBasedTriggerContext'
+ schedule: {
+ repeatingTimeIntervals: [
+ 'R/2022-05-31T23:30:00+01:00/P1D'
+ ]
+ timeZone: 'W. Europe Standard Time'
+ }
+ taggingCriteria: [
+ {
+ isDefault: true
+ taggingPriority: 99
+ tagInfo: {
+ id: 'Default_'
+ tagName: 'Default'
+ }
+ }
+ ]
+ }
+ }
+ {
+ isDefault: true
+ lifecycles: [
+ {
+ deleteAfter: {
+ duration: 'P7D'
+ objectType: 'AbsoluteDeleteOption'
+ }
+ sourceDataStore: {
+ dataStoreType: 'OperationalStore'
+ objectType: 'DataStoreInfoBase'
+ }
+ targetDataStoreCopySettings: []
+ }
+ ]
+ name: 'Default'
+ objectType: 'AzureRetentionRule'
+ }
+ ]
+ }
+ }
+ ]
+ enableDefaultTelemetry: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "dpbvwaf001"
+ },
+ // Non-required parameters
+ "azureMonitorAlertSettingsAlertsForAllJobFailures": {
+ "value": "Disabled"
+ },
+ "backupPolicies": {
+ "value": [
+ {
+ "name": "DefaultPolicy",
+ "properties": {
+ "datasourceTypes": [
+ "Microsoft.Compute/disks"
+ ],
+ "objectType": "BackupPolicy",
+ "policyRules": [
+ {
+ "backupParameters": {
+ "backupType": "Incremental",
+ "objectType": "AzureBackupParams"
+ },
+ "dataStore": {
+ "dataStoreType": "OperationalStore",
+ "objectType": "DataStoreInfoBase"
+ },
+ "name": "BackupDaily",
+ "objectType": "AzureBackupRule",
+ "trigger": {
+ "objectType": "ScheduleBasedTriggerContext",
+ "schedule": {
+ "repeatingTimeIntervals": [
+ "R/2022-05-31T23:30:00+01:00/P1D"
+ ],
+ "timeZone": "W. Europe Standard Time"
+ },
+ "taggingCriteria": [
+ {
+ "isDefault": true,
+ "taggingPriority": 99,
+ "tagInfo": {
+ "id": "Default_",
+ "tagName": "Default"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "isDefault": true,
+ "lifecycles": [
+ {
+ "deleteAfter": {
+ "duration": "P7D",
+ "objectType": "AbsoluteDeleteOption"
+ },
+ "sourceDataStore": {
+ "dataStoreType": "OperationalStore",
+ "objectType": "DataStoreInfoBase"
+ },
+ "targetDataStoreCopySettings": []
+ }
+ ],
+ "name": "Default",
+ "objectType": "AzureRetentionRule"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "enableDefaultTelemetry": {
+ "value": "
+ ## Parameters diff --git a/modules/data-protection/backup-vault/tests/e2e/waf-aligned/dependencies.bicep b/modules/data-protection/backup-vault/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..0f0755a6f4 --- /dev/null +++ b/modules/data-protection/backup-vault/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,16 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id diff --git a/modules/data-protection/backup-vault/tests/e2e/waf-aligned/main.test.bicep b/modules/data-protection/backup-vault/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..ef8e13b397 --- /dev/null +++ b/modules/data-protection/backup-vault/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,138 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-dataprotection.backupvaults-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'dpbvwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + azureMonitorAlertSettingsAlertsForAllJobFailures: 'Disabled' + managedIdentities: { + systemAssigned: true + } + backupPolicies: [ + { + name: 'DefaultPolicy' + properties: { + datasourceTypes: [ + 'Microsoft.Compute/disks' + ] + objectType: 'BackupPolicy' + policyRules: [ + { + backupParameters: { + backupType: 'Incremental' + objectType: 'AzureBackupParams' + } + dataStore: { + dataStoreType: 'OperationalStore' + objectType: 'DataStoreInfoBase' + } + name: 'BackupDaily' + objectType: 'AzureBackupRule' + trigger: { + objectType: 'ScheduleBasedTriggerContext' + schedule: { + repeatingTimeIntervals: [ + 'R/2022-05-31T23:30:00+01:00/P1D' + ] + timeZone: 'W. Europe Standard Time' + } + taggingCriteria: [ + { + isDefault: true + taggingPriority: 99 + tagInfo: { + id: 'Default_' + tagName: 'Default' + } + } + ] + } + } + { + isDefault: true + lifecycles: [ + { + deleteAfter: { + duration: 'P7D' + objectType: 'AbsoluteDeleteOption' + } + sourceDataStore: { + dataStoreType: 'OperationalStore' + objectType: 'DataStoreInfoBase' + } + targetDataStoreCopySettings: [] + } + ] + name: 'Default' + objectType: 'AzureRetentionRule' + } + ] + } + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/databricks/access-connector/README.md b/modules/databricks/access-connector/README.md index ad53643158..cc8cb19003 100644 --- a/modules/databricks/access-connector/README.md +++ b/modules/databricks/access-connector/README.md @@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -181,6 +182,110 @@ module accessConnector 'br:bicep/modules/databricks.access-connector:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module accessConnector 'br:bicep/modules/databricks.access-connector:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-dacwaf'
+ params: {
+ // Required parameters
+ name: 'dacwaf001'
+ // Non-required parameters
+ enableDefaultTelemetry: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "dacwaf001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "
+ ## Parameters diff --git a/modules/databricks/access-connector/tests/e2e/waf-aligned/dependencies.bicep b/modules/databricks/access-connector/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..b20bc53e8f --- /dev/null +++ b/modules/databricks/access-connector/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,16 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id diff --git a/modules/databricks/access-connector/tests/e2e/waf-aligned/main.test.bicep b/modules/databricks/access-connector/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..e61783c03c --- /dev/null +++ b/modules/databricks/access-connector/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,79 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-databricks.accessconnectors-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'dacwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + managedIdentities: { + systemAssigned: true + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + location: resourceGroup.location + } +} diff --git a/modules/databricks/workspace/README.md b/modules/databricks/workspace/README.md index 512cd9bc26..fcb2e26a86 100644 --- a/modules/databricks/workspace/README.md +++ b/modules/databricks/workspace/README.md @@ -32,6 +32,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -327,6 +328,252 @@ module workspace 'br:bicep/modules/databricks.workspace:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module workspace 'br:bicep/modules/databricks.workspace:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-dwwaf'
+ params: {
+ // Required parameters
+ name: 'dwwaf001'
+ // Non-required parameters
+ amlWorkspaceResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "dwwaf001"
+ },
+ // Non-required parameters
+ "amlWorkspaceResourceId": {
+ "value": "
+ ## Parameters diff --git a/modules/databricks/workspace/tests/e2e/waf-aligned/dependencies.bicep b/modules/databricks/workspace/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..4c074d6ae8 --- /dev/null +++ b/modules/databricks/workspace/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,368 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The name of the Key Vault for Disk Encryption to create.') +param keyVaultDiskName string + +@description('Required. The name of the Azure Machine Learning Workspace to create.') +param amlWorkspaceName string + +@description('Required. The name of the Load Balancer to create.') +param loadBalancerName string + +@description('Required. The name of the Network Security Group to create.') +param networkSecurityGroupName string + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +@description('Required. The name of the Application Insights Instanec to create.') +param applicationInsightsName string + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +var addressPrefix = '10.0.0.0/16' + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: true // Required by batch account + softDeleteRetentionInDays: 7 + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + + resource key 'keys@2022-07-01' = { + name: 'keyEncryptionKey' + properties: { + kty: 'RSA' + } + } +} + +resource keyVaultDisk 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultDiskName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: true // Required by batch account + softDeleteRetentionInDays: 7 + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + + resource key 'keys@2022-07-01' = { + name: 'keyEncryptionKeyDisk' + properties: { + kty: 'RSA' + } + } +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${keyVault::key.id}-${location}-${managedIdentity.id}-Key-Key-Vault-Crypto-User-RoleAssignment') + scope: keyVault::key + properties: { + principalId: '5167ea7a-355a-466f-ae8b-8ea60f718b35' // AzureDatabricks Enterprise Application Object Id + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') // Key Vault Crypto User + principalType: 'ServicePrincipal' + } +} + +resource amlPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${keyVault.id}-${location}-${managedIdentity.id}-Key-Vault-Contributor') + scope: keyVault + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') // Contributor + principalType: 'ServicePrincipal' + } +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_ZRS' + } + kind: 'StorageV2' + properties: {} +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: applicationInsightsName + location: location + kind: 'web' + properties: { + Application_Type: 'web' + } +} + +resource machineLearningWorkspace 'Microsoft.MachineLearningServices/workspaces@2023-04-01' = { + name: amlWorkspaceName + location: location + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + storageAccount: storageAccount.id + keyVault: keyVault.id + applicationInsights: applicationInsights.id + primaryUserAssignedIdentity: managedIdentity.id + } +} + +resource loadBalancer 'Microsoft.Network/loadBalancers@2023-04-01' = { + name: loadBalancerName + location: location + properties: { + backendAddressPools: [ + { + name: 'default' + } + ] + frontendIPConfigurations: [ + { + name: 'privateIPConfig1' + properties: { + subnet: { + id: virtualNetwork.properties.subnets[0].id + } + } + } + ] + } +} + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = { + name: networkSecurityGroupName + location: location + properties: { + securityRules: [ + { + name: 'Microsoft.Databricks-workspaces_UseOnly_databricks-worker-to-worker-inbound' + properties: { + description: 'Required for worker nodes communication within a cluster.' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: 'VirtualNetwork' + destinationAddressPrefix: 'VirtualNetwork' + access: 'Allow' + priority: 100 + direction: 'Inbound' + } + } + { + name: 'Microsoft.Databricks-workspaces_UseOnly_databricks-worker-to-databricks-webapp' + properties: { + description: 'Required for workers communication with Databricks Webapp.' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: 'VirtualNetwork' + destinationAddressPrefix: 'AzureDatabricks' + access: 'Allow' + priority: 100 + direction: 'Outbound' + } + } + { + name: 'Microsoft.Databricks-workspaces_UseOnly_databricks-worker-to-sql' + properties: { + description: 'Required for workers communication with Azure SQL services.' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '3306' + sourceAddressPrefix: 'VirtualNetwork' + destinationAddressPrefix: 'Sql' + access: 'Allow' + priority: 101 + direction: 'Outbound' + } + } + { + name: 'Microsoft.Databricks-workspaces_UseOnly_databricks-worker-to-storage' + properties: { + description: 'Required for workers communication with Azure Storage services.' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: 'VirtualNetwork' + destinationAddressPrefix: 'Storage' + access: 'Allow' + priority: 102 + direction: 'Outbound' + } + } + { + name: 'Microsoft.Databricks-workspaces_UseOnly_databricks-worker-to-worker-outbound' + properties: { + description: 'Required for worker nodes communication within a cluster.' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: 'VirtualNetwork' + destinationAddressPrefix: 'VirtualNetwork' + access: 'Allow' + priority: 103 + direction: 'Outbound' + } + } + { + name: 'Microsoft.Databricks-workspaces_UseOnly_databricks-worker-to-eventhub' + properties: { + description: 'Required for worker communication with Azure Eventhub services.' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '9093' + sourceAddressPrefix: 'VirtualNetwork' + destinationAddressPrefix: 'EventHub' + access: 'Allow' + priority: 104 + direction: 'Outbound' + } + } + ] + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 20, 0) + } + } + { + name: 'custom-public-subnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 20, 1) + networkSecurityGroup: { + id: networkSecurityGroup.id + } + delegations: [ + { + name: 'databricksDelegation' + properties: { + serviceName: 'Microsoft.Databricks/workspaces' + } + } + ] + } + } + { + name: 'custom-private-subnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 20, 2) + networkSecurityGroup: { + id: networkSecurityGroup.id + } + delegations: [ + { + name: 'databricksDelegation' + properties: { + serviceName: 'Microsoft.Databricks/workspaces' + } + } + ] + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.azuredatabricks.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +@description('The resource ID of the created Virtual Network Default Subnet.') +output defaultSubnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The name of the created Virtual Network Public Subnet.') +output customPublicSubnetName string = virtualNetwork.properties.subnets[1].name + +@description('The name of the created Virtual Network Private Subnet.') +output customPrivateSubnetName string = virtualNetwork.properties.subnets[2].name + +@description('The resource ID of the created Virtual Network.') +output virtualNetworkResourceId string = virtualNetwork.id + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id + +@description('The resource ID of the created Azure Machine Learning Workspace.') +output machineLearningWorkspaceResourceId string = machineLearningWorkspace.id + +@description('The resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id + +@description('The resource ID of the created Disk Key Vault.') +output keyVaultDiskResourceId string = keyVaultDisk.id + +@description('The resource ID of the created Load Balancer.') +output loadBalancerResourceId string = loadBalancer.id + +@description('The name of the created Load Balancer Backend Pool.') +output loadBalancerBackendPoolName string = loadBalancer.properties.backendAddressPools[0].name + +@description('The name of the created Key Vault encryption key.') +output keyVaultKeyName string = keyVault::key.name + +@description('The name of the created Key Vault Disk encryption key.') +output keyVaultDiskKeyName string = keyVaultDisk::key.name + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/databricks/workspace/tests/e2e/waf-aligned/main.test.bicep b/modules/databricks/workspace/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..4f74e4d560 --- /dev/null +++ b/modules/databricks/workspace/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,156 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-databricks.workspaces-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'dwwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + amlWorkspaceName: 'dep-${namePrefix}-aml-${serviceShort}' + applicationInsightsName: 'dep-${namePrefix}-appi-${serviceShort}' + loadBalancerName: 'dep-${namePrefix}-lb-${serviceShort}' + storageAccountName: 'dep${namePrefix}sa${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}' + // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total) + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' + keyVaultDiskName: 'dep-${namePrefix}-kve-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + diagnosticSettings: [ + { + name: 'customSetting' + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + logCategoriesAndGroups: [ + { + category: 'jobs' + } + { + category: 'notebook' + + } + ] + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + customerManagedKey: { + keyName: nestedDependencies.outputs.keyVaultKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + } + customerManagedKeyManagedDisk: { + keyName: nestedDependencies.outputs.keyVaultDiskKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultDiskResourceId + rotationToLatestKeyVersionEnabled: true + } + storageAccountName: 'sa${namePrefix}${serviceShort}001' + storageAccountSkuName: 'Standard_ZRS' + publicIpName: 'nat-gw-public-ip' + natGatewayName: 'nat-gateway' + prepareEncryption: true + requiredNsgRules: 'NoAzureDatabricksRules' + skuName: 'premium' + amlWorkspaceResourceId: nestedDependencies.outputs.machineLearningWorkspaceResourceId + customPrivateSubnetName: nestedDependencies.outputs.customPrivateSubnetName + customPublicSubnetName: nestedDependencies.outputs.customPublicSubnetName + publicNetworkAccess: 'Disabled' + disablePublicIp: true + loadBalancerResourceId: nestedDependencies.outputs.loadBalancerResourceId + loadBalancerBackendPoolName: nestedDependencies.outputs.loadBalancerBackendPoolName + customVirtualNetworkResourceId: nestedDependencies.outputs.virtualNetworkResourceId + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + subnetResourceId: nestedDependencies.outputs.defaultSubnetResourceId + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + managedResourceGroupResourceId: '${subscription().id}/resourceGroups/rg-${resourceGroupName}-managed' + requireInfrastructureEncryption: true + vnetAddressPrefix: '10.100' + location: resourceGroup.location + } +} diff --git a/modules/desktop-virtualization/application-group/README.md b/modules/desktop-virtualization/application-group/README.md index 83aa677d85..22947a3ef1 100644 --- a/modules/desktop-virtualization/application-group/README.md +++ b/modules/desktop-virtualization/application-group/README.md @@ -30,6 +30,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -251,6 +252,170 @@ module applicationGroup 'br:bicep/modules/desktop-virtualization.application-gro
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module applicationGroup 'br:bicep/modules/desktop-virtualization.application-group:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-dvagwaf'
+ params: {
+ // Required parameters
+ applicationGroupType: 'RemoteApp'
+ hostpoolName: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "applicationGroupType": {
+ "value": "RemoteApp"
+ },
+ "hostpoolName": {
+ "value": "
+ ## Parameters diff --git a/modules/desktop-virtualization/application-group/tests/e2e/waf-aligned/dependencies.bicep b/modules/desktop-virtualization/application-group/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..41ca94022b --- /dev/null +++ b/modules/desktop-virtualization/application-group/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,29 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Host Pool to create.') +param hostPoolName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource hostPool 'Microsoft.DesktopVirtualization/hostPools@2022-09-09' = { + name: hostPoolName + location: location + properties: { + hostPoolType: 'Pooled' + loadBalancerType: 'BreadthFirst' + preferredAppGroupType: 'Desktop' + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The name of the created Host Pool.') +output hostPoolName string = hostPool.name diff --git a/modules/desktop-virtualization/application-group/tests/e2e/waf-aligned/main.test.bicep b/modules/desktop-virtualization/application-group/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..eb507bfeaf --- /dev/null +++ b/modules/desktop-virtualization/application-group/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,119 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-desktopvirtualization.applicationgroups-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'dvagwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + hostPoolName: 'dep-${namePrefix}-hp-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + applicationGroupType: 'RemoteApp' + hostpoolName: nestedDependencies.outputs.hostPoolName + applications: [ + { + commandLineArguments: '' + commandLineSetting: 'DoNotAllow' + description: 'Notepad by ARM template' + filePath: 'C:\\Windows\\System32\\notepad.exe' + friendlyName: 'Notepad' + iconIndex: 0 + iconPath: 'C:\\Windows\\System32\\notepad.exe' + name: 'notepad' + showInPortal: true + } + { + filePath: 'C:\\Program Files\\Windows NT\\Accessories\\wordpad.exe' + friendlyName: 'Wordpad' + name: 'wordpad' + } + ] + description: 'This is my first Remote Applications bundle' + diagnosticSettings: [ + { + name: 'customSetting' + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + friendlyName: 'Remote Applications 1' + location: location + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/desktop-virtualization/host-pool/README.md b/modules/desktop-virtualization/host-pool/README.md index cc5703c6ab..37af321393 100644 --- a/modules/desktop-virtualization/host-pool/README.md +++ b/modules/desktop-virtualization/host-pool/README.md @@ -29,6 +29,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -284,6 +285,212 @@ module hostPool 'br:bicep/modules/desktop-virtualization.host-pool:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module hostPool 'br:bicep/modules/desktop-virtualization.host-pool:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-dvhpwaf'
+ params: {
+ // Required parameters
+ name: 'dvhpwaf001'
+ // Non-required parameters
+ agentUpdate: {
+ maintenanceWindows: [
+ {
+ dayOfWeek: 'Friday'
+ hour: 7
+ }
+ {
+ dayOfWeek: 'Saturday'
+ hour: 8
+ }
+ ]
+ maintenanceWindowTimeZone: 'Alaskan Standard Time'
+ type: 'Scheduled'
+ useSessionHostLocalTime: false
+ }
+ customRdpProperty: 'audiocapturemode:i:1;audiomode:i:0;drivestoredirect:s:;redirectclipboard:i:1;redirectcomports:i:1;redirectprinters:i:1;redirectsmartcards:i:1;screen mode id:i:2;'
+ description: 'My first AVD Host Pool'
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "dvhpwaf001"
+ },
+ // Non-required parameters
+ "agentUpdate": {
+ "value": {
+ "maintenanceWindows": [
+ {
+ "dayOfWeek": "Friday",
+ "hour": 7
+ },
+ {
+ "dayOfWeek": "Saturday",
+ "hour": 8
+ }
+ ],
+ "maintenanceWindowTimeZone": "Alaskan Standard Time",
+ "type": "Scheduled",
+ "useSessionHostLocalTime": false
+ }
+ },
+ "customRdpProperty": {
+ "value": "audiocapturemode:i:1;audiomode:i:0;drivestoredirect:s:;redirectclipboard:i:1;redirectcomports:i:1;redirectprinters:i:1;redirectsmartcards:i:1;screen mode id:i:2;"
+ },
+ "description": {
+ "value": "My first AVD Host Pool"
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "
+ ## Parameters diff --git a/modules/desktop-virtualization/host-pool/tests/e2e/waf-aligned/dependencies.bicep b/modules/desktop-virtualization/host-pool/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..a7f42aee7b --- /dev/null +++ b/modules/desktop-virtualization/host-pool/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/desktop-virtualization/host-pool/tests/e2e/waf-aligned/main.test.bicep b/modules/desktop-virtualization/host-pool/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..6499c1f67f --- /dev/null +++ b/modules/desktop-virtualization/host-pool/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,135 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-desktopvirtualization.hostpools-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'dvhpwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + customRdpProperty: 'audiocapturemode:i:1;audiomode:i:0;drivestoredirect:s:;redirectclipboard:i:1;redirectcomports:i:1;redirectprinters:i:1;redirectsmartcards:i:1;screen mode id:i:2;' + diagnosticSettings: [ + { + name: 'customSetting' + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + description: 'My first AVD Host Pool' + friendlyName: 'AVDv2' + type: 'Pooled' + loadBalancerType: 'BreadthFirst' + location: location + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + maxSessionLimit: 99999 + personalDesktopAssignmentType: 'Automatic' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + vmTemplate: { + customImageId: null + domain: 'domainname.onmicrosoft.com' + galleryImageOffer: 'office-365' + galleryImagePublisher: 'microsoftwindowsdesktop' + galleryImageSKU: '20h1-evd-o365pp' + imageType: 'Gallery' + imageUri: null + namePrefix: 'avdv2' + osDiskType: 'StandardSSD_LRS' + useManagedDisks: true + vmSize: { + cores: 2 + id: 'Standard_D2s_v3' + ram: 8 + } + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + agentUpdate: { + type: 'Scheduled' + useSessionHostLocalTime: false + maintenanceWindowTimeZone: 'Alaskan Standard Time' + maintenanceWindows: [ + { + hour: 7 + dayOfWeek: 'Friday' + } + { + hour: 8 + dayOfWeek: 'Saturday' + } + ] + } + } +} diff --git a/modules/desktop-virtualization/scaling-plan/README.md b/modules/desktop-virtualization/scaling-plan/README.md index 0983c6dbbc..96f2d667e4 100644 --- a/modules/desktop-virtualization/scaling-plan/README.md +++ b/modules/desktop-virtualization/scaling-plan/README.md @@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -267,6 +268,196 @@ module scalingPlan 'br:bicep/modules/desktop-virtualization.scaling-plan:1.0.0'
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module scalingPlan 'br:bicep/modules/desktop-virtualization.scaling-plan:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-dvspwaf'
+ params: {
+ // Required parameters
+ name: 'dvspwaf001'
+ // Non-required parameters
+ description: 'My Scaling Plan Description'
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "dvspwaf001"
+ },
+ // Non-required parameters
+ "description": {
+ "value": "My Scaling Plan Description"
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "
+ ## Parameters diff --git a/modules/desktop-virtualization/scaling-plan/tests/e2e/waf-aligned/dependencies.bicep b/modules/desktop-virtualization/scaling-plan/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..a7f42aee7b --- /dev/null +++ b/modules/desktop-virtualization/scaling-plan/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/desktop-virtualization/scaling-plan/tests/e2e/waf-aligned/main.test.bicep b/modules/desktop-virtualization/scaling-plan/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..0c02e7560e --- /dev/null +++ b/modules/desktop-virtualization/scaling-plan/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,133 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-desktopvirtualization.scalingplans-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'dvspwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + diagnosticSettings: [ + { + name: 'customSetting' + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + hostPoolType: 'Pooled' + friendlyName: 'My Scaling Plan' + description: 'My Scaling Plan Description' + schedules: [ { + rampUpStartTime: { + hour: 7 + minute: 0 + } + peakStartTime: { + hour: 9 + minute: 0 + } + rampDownStartTime: { + hour: 18 + minute: 0 + } + offPeakStartTime: { + hour: 20 + minute: 0 + } + name: 'weekdays_schedule' + daysOfWeek: [ + 'Monday' + 'Tuesday' + 'Wednesday' + 'Thursday' + 'Friday' + ] + rampUpLoadBalancingAlgorithm: 'DepthFirst' + rampUpMinimumHostsPct: 20 + rampUpCapacityThresholdPct: 60 + peakLoadBalancingAlgorithm: 'DepthFirst' + rampDownLoadBalancingAlgorithm: 'DepthFirst' + rampDownMinimumHostsPct: 10 + rampDownCapacityThresholdPct: 90 + rampDownForceLogoffUsers: true + rampDownWaitTimeMinutes: 30 + rampDownNotificationMessage: 'You will be logged off in 30 min. Make sure to save your work.' + rampDownStopHostsWhen: 'ZeroSessions' + offPeakLoadBalancingAlgorithm: 'DepthFirst' + } + ] + } +} diff --git a/modules/desktop-virtualization/workspace/README.md b/modules/desktop-virtualization/workspace/README.md index 2fab487621..641cdb7674 100644 --- a/modules/desktop-virtualization/workspace/README.md +++ b/modules/desktop-virtualization/workspace/README.md @@ -29,6 +29,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -204,6 +205,132 @@ module workspace 'br:bicep/modules/desktop-virtualization.workspace:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module workspace 'br:bicep/modules/desktop-virtualization.workspace:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-dvwwaf'
+ params: {
+ // Required parameters
+ name: 'dvwwaf001'
+ // Non-required parameters
+ appGroupResourceIds: [
+ '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "dvwwaf001"
+ },
+ // Non-required parameters
+ "appGroupResourceIds": {
+ "value": [
+ "
+ ## Parameters diff --git a/modules/desktop-virtualization/workspace/tests/e2e/waf-aligned/dependencies.bicep b/modules/desktop-virtualization/workspace/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..8e753087b2 --- /dev/null +++ b/modules/desktop-virtualization/workspace/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,41 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Application Group to create.') +param applicationGroupName string + +@description('Required. The name of the Host Pool to create.') +param hostPoolName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource hostPool 'Microsoft.DesktopVirtualization/hostPools@2022-09-09' = { + name: hostPoolName + location: location + properties: { + hostPoolType: 'Pooled' + loadBalancerType: 'BreadthFirst' + preferredAppGroupType: 'Desktop' + } +} + +resource applicationGroup 'Microsoft.DesktopVirtualization/applicationGroups@2022-09-09' = { + name: applicationGroupName + location: location + properties: { + applicationGroupType: 'Desktop' + hostPoolArmPath: hostPool.id + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Application Group.') +output applicationGroupResourceId string = applicationGroup.id diff --git a/modules/desktop-virtualization/workspace/tests/e2e/waf-aligned/main.test.bicep b/modules/desktop-virtualization/workspace/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..e6907c5ee2 --- /dev/null +++ b/modules/desktop-virtualization/workspace/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,103 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-desktopvirtualization.workspaces-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'dvwwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + applicationGroupName: 'dep-${namePrefix}-appGroup-${serviceShort}' + hostPoolName: 'dep-${namePrefix}-hp-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + appGroupResourceIds: [ + nestedDependencies.outputs.applicationGroupResourceId + ] + diagnosticSettings: [ + { + name: 'customSetting' + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + location: location + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + description: 'This is my first AVD Workspace' + friendlyName: 'My first AVD Workspace' + } +} diff --git a/modules/dev-test-lab/lab/README.md b/modules/dev-test-lab/lab/README.md index b7b777f88b..f4444676bb 100644 --- a/modules/dev-test-lab/lab/README.md +++ b/modules/dev-test-lab/lab/README.md @@ -34,6 +34,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -625,6 +626,548 @@ module lab 'br:bicep/modules/dev-test-lab.lab:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module lab 'br:bicep/modules/dev-test-lab.lab:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-dtllwaf'
+ params: {
+ // Required parameters
+ name: 'dtllwaf001'
+ // Non-required parameters
+ announcement: {
+ enabled: 'Enabled'
+ expirationDate: '2025-12-30T13:00:00Z'
+ markdown: 'DevTest Lab announcement text.
New line. It also supports Markdown'
+ title: 'DevTest announcement title'
+ }
+ artifactsources: [
+ {
+ branchRef: 'master'
+ displayName: 'Public Artifact Repo'
+ folderPath: '/Artifacts'
+ name: 'Public Repo'
+ sourceType: 'GitHub'
+ status: 'Disabled'
+ uri: 'https://github.com/Azure/azure-devtestlab.git'
+ }
+ {
+ armTemplateFolderPath: '/Environments'
+ branchRef: 'master'
+ displayName: 'Public Environment Repo'
+ name: 'Public Environment Repo'
+ sourceType: 'GitHub'
+ status: 'Disabled'
+ uri: 'https://github.com/Azure/azure-devtestlab.git'
+ }
+ ]
+ artifactsStorageAccount: '
New line. It also supports Markdown'
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ labName: 'dtllwaf001'
+ resourceType: 'DevTest Lab'
+ }
+ virtualnetworks: [
+ {
+ allowedSubnets: [
+ {
+ allowPublicIp: 'Allow'
+ labSubnetName: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "dtllwaf001"
+ },
+ // Non-required parameters
+ "announcement": {
+ "value": {
+ "enabled": "Enabled",
+ "expirationDate": "2025-12-30T13:00:00Z",
+ "markdown": "DevTest Lab announcement text.
New line. It also supports Markdown",
+ "title": "DevTest announcement title"
+ }
+ },
+ "artifactsources": {
+ "value": [
+ {
+ "branchRef": "master",
+ "displayName": "Public Artifact Repo",
+ "folderPath": "/Artifacts",
+ "name": "Public Repo",
+ "sourceType": "GitHub",
+ "status": "Disabled",
+ "uri": "https://github.com/Azure/azure-devtestlab.git"
+ },
+ {
+ "armTemplateFolderPath": "/Environments",
+ "branchRef": "master",
+ "displayName": "Public Environment Repo",
+ "name": "Public Environment Repo",
+ "sourceType": "GitHub",
+ "status": "Disabled",
+ "uri": "https://github.com/Azure/azure-devtestlab.git"
+ }
+ ]
+ },
+ "artifactsStorageAccount": {
+ "value": "
New line. It also supports Markdown"
+ }
+ },
+ "tags": {
+ "value": {
+ "hidden-title": "This is visible in the resource name",
+ "labName": "dtllwaf001",
+ "resourceType": "DevTest Lab"
+ }
+ },
+ "virtualnetworks": {
+ "value": [
+ {
+ "allowedSubnets": [
+ {
+ "allowPublicIp": "Allow",
+ "labSubnetName": "
+
## Parameters
diff --git a/modules/dev-test-lab/lab/tests/e2e/waf-aligned/dependencies.bicep b/modules/dev-test-lab/lab/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..10d28c8ae6
--- /dev/null
+++ b/modules/dev-test-lab/lab/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,134 @@
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the Disk Encryption Set to create.')
+param diskEncryptionSetName string
+
+@description('Required. The name of the Key Vault to create.')
+param keyVaultName string
+
+@description('Required. The name of the Storage Account to create.')
+param storageAccountName string
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
+ name: keyVaultName
+ location: location
+ properties: {
+ sku: {
+ family: 'A'
+ name: 'standard'
+ }
+ tenantId: tenant().tenantId
+ enablePurgeProtection: true // Required for encrption to work
+ softDeleteRetentionInDays: 7
+ enabledForTemplateDeployment: true
+ enabledForDiskEncryption: true
+ enabledForDeployment: true
+ enableRbacAuthorization: true
+ accessPolicies: []
+ }
+
+ resource key 'keys@2022-07-01' = {
+ name: 'encryptionKey'
+ properties: {
+ kty: 'RSA'
+ }
+ }
+}
+
+resource diskEncryptionSet 'Microsoft.Compute/diskEncryptionSets@2021-04-01' = {
+ name: diskEncryptionSetName
+ location: location
+ identity: {
+ type: 'SystemAssigned'
+ }
+ properties: {
+ activeKey: {
+ sourceVault: {
+ id: keyVault.id
+ }
+ keyUrl: keyVault::key.properties.keyUriWithVersion
+ }
+ encryptionType: 'EncryptionAtRestWithCustomerKey'
+ }
+}
+
+resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
+ name: guid('msi-${keyVault.id}-${location}-${diskEncryptionSet.id}-KeyVault-Key-Read-RoleAssignment')
+ scope: keyVault
+ properties: {
+ principalId: diskEncryptionSet.identity.principalId
+ roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6') // Key Vault Crypto Service Encryption User
+ principalType: 'ServicePrincipal'
+ }
+}
+
+resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = {
+ name: storageAccountName
+ location: location
+ kind: 'StorageV2'
+ sku: {
+ name: 'Standard_LRS'
+ }
+ properties: {
+ allowBlobPublicAccess: false
+ publicNetworkAccess: 'Disabled'
+ }
+}
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ }
+ }
+ ]
+ }
+}
+
+@description('The name of the created Virtual Network.')
+output virtualNetworkName string = virtualNetwork.name
+
+@description('The resource ID of the created Virtual Network.')
+output virtualNetworkResourceId string = virtualNetwork.id
+
+@description('The name of the created Virtual Network Subnet.')
+output subnetName string = virtualNetwork.properties.subnets[0].name
+
+@description('The resource ID of the created Virtual Network Subnet.')
+output subnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
+
+@description('The resource ID of the created Disk Encryption Set.')
+output diskEncryptionSetResourceId string = diskEncryptionSet.id
+
+@description('The resource ID of the created Storage Account.')
+output storageAccountResourceId string = storageAccount.id
diff --git a/modules/dev-test-lab/lab/tests/e2e/waf-aligned/main.test.bicep b/modules/dev-test-lab/lab/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..5c1f2064a6
--- /dev/null
+++ b/modules/dev-test-lab/lab/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,286 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-devtestlab.labs-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'dtllwaf'
+
+@description('Generated. Used as a basis for unique resource names.')
+param baseTime string = utcNow('u')
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total)
+ keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}'
+ diskEncryptionSetName: 'dep-${namePrefix}-des-${serviceShort}'
+ storageAccountName: 'dep${namePrefix}sa${serviceShort}'
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ location: resourceGroup.location
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ resourceType: 'DevTest Lab'
+ labName: '${namePrefix}${serviceShort}001'
+ }
+ announcement: {
+ enabled: 'Enabled'
+ expirationDate: '2025-12-30T13:00:00.000Z'
+ markdown: 'DevTest Lab announcement text.
New line. It also supports Markdown'
+ title: 'DevTest announcement title'
+ }
+ environmentPermission: 'Contributor'
+ extendedProperties: {
+ RdpConnectionType: '7'
+ }
+ labStorageType: 'Premium'
+ artifactsStorageAccount: nestedDependencies.outputs.storageAccountResourceId
+ premiumDataDisks: 'Enabled'
+ support: {
+ enabled: 'Enabled'
+ markdown: 'DevTest Lab support text.
New line. It also supports Markdown'
+ }
+ managedIdentities: {
+ userAssignedResourcesIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ managementIdentitiesResourceIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ vmCreationResourceGroupId: resourceGroup.id
+ browserConnect: 'Enabled'
+ disableAutoUpgradeCseMinorVersion: true
+ isolateLabResources: 'Enabled'
+ encryptionType: 'EncryptionAtRestWithCustomerKey'
+ encryptionDiskEncryptionSetId: nestedDependencies.outputs.diskEncryptionSetResourceId
+ virtualnetworks: [
+ {
+ name: nestedDependencies.outputs.virtualNetworkName
+ externalProviderResourceId: nestedDependencies.outputs.virtualNetworkResourceId
+ description: 'lab virtual network description'
+ allowedSubnets: [
+ {
+ labSubnetName: nestedDependencies.outputs.subnetName
+ resourceId: nestedDependencies.outputs.subnetResourceId
+ allowPublicIp: 'Allow'
+ }
+ ]
+ subnetOverrides: [
+ {
+ labSubnetName: nestedDependencies.outputs.subnetName
+ resourceId: nestedDependencies.outputs.subnetResourceId
+ useInVmCreationPermission: 'Allow'
+ usePublicIpAddressPermission: 'Allow'
+ sharedPublicIpAddressConfiguration: {
+ allowedPorts: [
+ {
+ transportProtocol: 'Tcp'
+ backendPort: 3389
+ }
+ {
+ transportProtocol: 'Tcp'
+ backendPort: 22
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ policies: [
+ {
+ name: nestedDependencies.outputs.subnetName
+ evaluatorType: 'MaxValuePolicy'
+ factData: nestedDependencies.outputs.subnetResourceId
+ factName: 'UserOwnedLabVmCountInSubnet'
+ threshold: '1'
+ }
+ {
+ name: 'MaxVmsAllowedPerUser'
+ evaluatorType: 'MaxValuePolicy'
+ factName: 'UserOwnedLabVmCount'
+ threshold: '2'
+ }
+ {
+ name: 'MaxPremiumVmsAllowedPerUser'
+ evaluatorType: 'MaxValuePolicy'
+ factName: 'UserOwnedLabPremiumVmCount'
+ status: 'Disabled'
+ threshold: '1'
+ }
+ {
+ name: 'MaxVmsAllowedPerLab'
+ evaluatorType: 'MaxValuePolicy'
+ factName: 'LabVmCount'
+ threshold: '3'
+ }
+ {
+ name: 'MaxPremiumVmsAllowedPerLab'
+ evaluatorType: 'MaxValuePolicy'
+ factName: 'LabPremiumVmCount'
+ threshold: '2'
+ }
+ {
+ name: 'AllowedVmSizesInLab'
+ evaluatorType: 'AllowedValuesPolicy'
+ factData: ''
+ factName: 'LabVmSize'
+ threshold: ' ${string('["Basic_A0","Basic_A1"]')}'
+ status: 'Enabled'
+ }
+ {
+ name: 'ScheduleEditPermission'
+ evaluatorType: 'AllowedValuesPolicy'
+ factName: 'ScheduleEditPermission'
+ threshold: ' ${string('["None","Modify"]')}'
+ }
+ {
+ name: 'GalleryImage'
+ evaluatorType: 'AllowedValuesPolicy'
+ factName: 'GalleryImage'
+ threshold: ' ${string('["{\\"offer\\":\\"WindowsServer\\",\\"publisher\\":\\"MicrosoftWindowsServer\\",\\"sku\\":\\"2019-Datacenter-smalldisk\\",\\"osType\\":\\"Windows\\",\\"version\\":\\"latest\\"}","{\\"offer\\":\\"WindowsServer\\",\\"publisher\\":\\"MicrosoftWindowsServer\\",\\"sku\\":\\"2022-datacenter-smalldisk\\",\\"osType\\":\\"Windows\\",\\"version\\":\\"latest\\"}"]')}'
+ }
+ {
+ name: 'EnvironmentTemplate'
+ description: 'Public Environment Policy'
+ evaluatorType: 'AllowedValuesPolicy'
+ factName: 'EnvironmentTemplate'
+ threshold: ' ${string('[""]')}'
+ }
+ ]
+ schedules: [
+ {
+ name: 'LabVmsShutdown'
+ taskType: 'LabVmsShutdownTask'
+ status: 'Enabled'
+ timeZoneId: 'AUS Eastern Standard Time'
+ dailyRecurrence: {
+ time: '0000'
+ }
+ notificationSettingsStatus: 'Enabled'
+ notificationSettingsTimeInMinutes: 30
+ }
+ {
+ name: 'LabVmAutoStart'
+ taskType: 'LabVmsStartupTask'
+ status: 'Enabled'
+ timeZoneId: 'AUS Eastern Standard Time'
+ weeklyRecurrence: {
+ time: '0700'
+ weekdays: [
+ 'Monday'
+ 'Tuesday'
+ 'Wednesday'
+ 'Thursday'
+ 'Friday'
+ ]
+ }
+ }
+ ]
+ notificationchannels: [
+ {
+ name: 'autoShutdown'
+ description: 'Integration configured for auto-shutdown'
+ events: [
+ {
+ eventName: 'AutoShutdown'
+ }
+ ]
+ emailRecipient: 'mail@contosodtlmail.com'
+ webHookUrl: 'https://webhook.contosotest.com'
+ notificationLocale: 'en'
+ }
+ {
+ name: 'costThreshold'
+ events: [
+ {
+ eventName: 'Cost'
+ }
+ ]
+ webHookUrl: 'https://webhook.contosotest.com'
+ }
+ ]
+ artifactsources: [
+ {
+ name: 'Public Repo'
+ displayName: 'Public Artifact Repo'
+ status: 'Disabled'
+ uri: 'https://github.com/Azure/azure-devtestlab.git'
+ sourceType: 'GitHub'
+ branchRef: 'master'
+ folderPath: '/Artifacts'
+ }
+ {
+ name: 'Public Environment Repo'
+ displayName: 'Public Environment Repo'
+ status: 'Disabled'
+ uri: 'https://github.com/Azure/azure-devtestlab.git'
+ sourceType: 'GitHub'
+ branchRef: 'master'
+ armTemplateFolderPath: '/Environments'
+ }
+ ]
+ costs: {
+ status: 'Enabled'
+ cycleType: 'CalendarMonth'
+ target: 450
+ thresholdValue100DisplayOnChart: 'Enabled'
+ thresholdValue100SendNotificationWhenExceeded: 'Enabled'
+ }
+ }
+}
diff --git a/modules/digital-twins/digital-twins-instance/README.md b/modules/digital-twins/digital-twins-instance/README.md
index bed016932f..574c196c63 100644
--- a/modules/digital-twins/digital-twins-instance/README.md
+++ b/modules/digital-twins/digital-twins-instance/README.md
@@ -32,6 +32,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -261,6 +262,186 @@ module digitalTwinsInstance 'br:bicep/modules/digital-twins.digital-twins-instan
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module digitalTwinsInstance 'br:bicep/modules/digital-twins.digital-twins-instance:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-dtdtiwaf'
+ params: {
+ // Required parameters
+ name: 'dtdtiwaf001'
+ // Non-required parameters
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "dtdtiwaf001"
+ },
+ // Non-required parameters
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "
+ ## Parameters diff --git a/modules/digital-twins/digital-twins-instance/tests/e2e/waf-aligned/dependencies.bicep b/modules/digital-twins/digital-twins-instance/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..87c0cf8a6f --- /dev/null +++ b/modules/digital-twins/digital-twins-instance/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,162 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Event Hub Namespace to create.') +param eventHubNamespaceName string + +@description('Required. The name of the Event Hub to create.') +param eventHubName string + +@description('Required. Service Bus name') +param serviceBusName string + +@description('Required. Event Grid Domain name.') +param eventGridDomainName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + serviceEndpoints: [ + { + service: 'Microsoft.KeyVault' + } + ] + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.digitaltwins.azure.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource eventHubNamespace 'Microsoft.EventHub/namespaces@2022-10-01-preview' = { + name: eventHubNamespaceName + location: location + properties: { + zoneRedundant: false + isAutoInflateEnabled: false + maximumThroughputUnits: 0 + } + + resource eventHub 'eventhubs@2022-10-01-preview' = { + name: eventHubName + } +} + +resource serviceBus 'Microsoft.ServiceBus/namespaces@2022-10-01-preview' = { + name: serviceBusName + location: location + properties: { + zoneRedundant: false + } + + resource topic 'topics@2022-10-01-preview' = { + name: 'topic' + } +} + +resource eventGridDomain 'Microsoft.EventGrid/domains@2022-06-15' = { + name: eventGridDomainName + location: location + properties: { + disableLocalAuth: false + } + + resource topic 'topics@2022-06-15' = { + name: 'topic' + } +} + +resource eventHubNamespaceRbacAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(managedIdentity.id, 'evhrbacAssignment') + scope: eventHubNamespace + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2b629674-e913-4c01-ae53-ef4638d8f975') //Azure Event Hubs Data Sender + principalId: managedIdentity.properties.principalId + principalType: 'ServicePrincipal' + } +} + +resource serviceBusRbacAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(managedIdentity.id, 'sbrbacAssignment') + scope: serviceBus + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39') //Azure Service Bus Data Sender + principalId: managedIdentity.properties.principalId + principalType: 'ServicePrincipal' + } +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalResourceId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id + +@description('The name of the Event Hub Namespace.') +output eventhubNamespaceName string = eventHubNamespace.name + +@description('The resource ID of the created Event Hub Namespace.') +output eventHubResourceId string = eventHubNamespace::eventHub.id + +@description('The name of the Event Hub.') +output eventhubName string = eventHubNamespace::eventHub.name + +@description('The name of the Service Bus Namespace.') +output serviceBusName string = serviceBus.name + +@description('The name of the Service Bus Topic.') +output serviceBusTopicName string = serviceBus::topic.name + +@description('The Event Grid endpoint uri.') +output eventGridEndpoint string = eventGridDomain.properties.endpoint + +@description('The resource ID of the created Event Grid Topic.') +output eventGridTopicResourceId string = eventGridDomain::topic.id + +@description('The resource ID of the created Event Grid Domain.') +output eventGridDomainResourceId string = eventGridDomain.id + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id diff --git a/modules/digital-twins/digital-twins-instance/tests/e2e/waf-aligned/main.test.bicep b/modules/digital-twins/digital-twins-instance/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..2c2f2e28ca --- /dev/null +++ b/modules/digital-twins/digital-twins-instance/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,132 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-digitaltwins.digitaltwinsinstances-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'dtdtiwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + eventHubName: 'dt-${uniqueString(serviceShort)}-evh-01' + eventHubNamespaceName: 'dt-${uniqueString(serviceShort)}-evhns-01' + serviceBusName: 'dt-${uniqueString(serviceShort)}-sb-01' + eventGridDomainName: 'dt-${uniqueString(serviceShort)}-evg-01' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}03' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${uniqueString(serviceShort)}-evh-01' + eventHubNamespaceName: 'dep-${uniqueString(serviceShort)}-evh-01' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + eventHubEndpoint: { + authenticationType: 'IdentityBased' + endpointUri: 'sb://${nestedDependencies.outputs.eventhubNamespaceName}.servicebus.windows.net/' + entityPath: nestedDependencies.outputs.eventhubName + userAssignedIdentity: nestedDependencies.outputs.managedIdentityResourceId + } + serviceBusEndpoint: { + authenticationType: 'IdentityBased' + endpointUri: 'sb://${nestedDependencies.outputs.serviceBusName}.servicebus.windows.net/' + entityPath: nestedDependencies.outputs.serviceBusTopicName + userAssignedIdentity: nestedDependencies.outputs.managedIdentityResourceId + } + eventGridEndpoint: { + eventGridDomainId: nestedDependencies.outputs.eventGridDomainResourceId + topicEndpoint: nestedDependencies.outputs.eventGridEndpoint + } + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + userAssignedIdentities: { + '${nestedDependencies.outputs.managedIdentityResourceId}': {} + } + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + subnetResourceId: nestedDependencies.outputs.subnetResourceId + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalResourceId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/event-grid/domain/README.md b/modules/event-grid/domain/README.md index be9e32e179..38f46a6a77 100644 --- a/modules/event-grid/domain/README.md +++ b/modules/event-grid/domain/README.md @@ -33,6 +33,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) - [Pe](#example-3-pe) +- [WAF-aligned](#example-4-waf-aligned) ### Example 1: _Using only defaults_ @@ -335,6 +336,174 @@ module domain 'br:bicep/modules/event-grid.domain:1.0.0' = {
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module domain 'br:bicep/modules/event-grid.domain:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-egdwaf'
+ params: {
+ // Required parameters
+ name: 'egdwaf001'
+ // Non-required parameters
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "egdwaf001"
+ },
+ // Non-required parameters
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "
+ ## Parameters diff --git a/modules/event-grid/domain/tests/e2e/waf-aligned/dependencies.bicep b/modules/event-grid/domain/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..8ba0c35f61 --- /dev/null +++ b/modules/event-grid/domain/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,60 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.eventgrid.azure.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id diff --git a/modules/event-grid/domain/tests/e2e/waf-aligned/main.test.bicep b/modules/event-grid/domain/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..bdb9c0b651 --- /dev/null +++ b/modules/event-grid/domain/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,124 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-eventgrid.domains-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'egdwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + inboundIpRules: [ + { + action: 'Allow' + ipMask: '40.74.28.0/23' + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + service: 'domain' + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + topics: [ + '${namePrefix}-topic-${serviceShort}001' + ] + } +} diff --git a/modules/event-grid/system-topic/README.md b/modules/event-grid/system-topic/README.md index 526c04d4a7..e46107cf3b 100644 --- a/modules/event-grid/system-topic/README.md +++ b/modules/event-grid/system-topic/README.md @@ -30,6 +30,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -269,6 +270,188 @@ module systemTopic 'br:bicep/modules/event-grid.system-topic:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module systemTopic 'br:bicep/modules/event-grid.system-topic:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-egstwaf'
+ params: {
+ // Required parameters
+ name: 'egstwaf001'
+ source: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "egstwaf001"
+ },
+ "source": {
+ "value": "
+ ## Parameters diff --git a/modules/event-grid/system-topic/tests/e2e/waf-aligned/dependencies.bicep b/modules/event-grid/system-topic/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..9b192272d4 --- /dev/null +++ b/modules/event-grid/system-topic/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,42 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +@description('Required. The name of the Storage Queue to create.') +param storageQueueName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + + resource queueService 'queueServices@2022-09-01' = { + name: 'default' + + resource queue 'queues@2022-09-01' = { + name: storageQueueName + } + } +} + +@description('The name of the created Storage Account Queue.') +output queueName string = storageAccount::queueService::queue.name + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Storage Account.') +output storageAccountResourceId string = storageAccount.id diff --git a/modules/event-grid/system-topic/tests/e2e/waf-aligned/main.test.bicep b/modules/event-grid/system-topic/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..0ca8feb5b6 --- /dev/null +++ b/modules/event-grid/system-topic/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,129 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-eventgrid.systemtopics-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'egstwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + storageAccountName: 'dep${namePrefix}sa${serviceShort}' + storageQueueName: 'dep${namePrefix}sq${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + source: nestedDependencies.outputs.storageAccountResourceId + topicType: 'Microsoft.Storage.StorageAccounts' + eventSubscriptions: [ { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + expirationTimeUtc: '2099-01-01T11:00:21.715Z' + filter: { + isSubjectCaseSensitive: false + enableAdvancedFilteringOnArrays: true + } + retryPolicy: { + maxDeliveryAttempts: 10 + eventTimeToLive: '120' + } + eventDeliverySchema: 'CloudEventSchemaV1_0' + destination: { + endpointType: 'StorageQueue' + properties: { + resourceId: nestedDependencies.outputs.storageAccountResourceId + queueMessageTimeToLiveInSeconds: 86400 + queueName: nestedDependencies.outputs.queueName + } + } + } ] + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + managedIdentities: { + systemAssigned: true + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/event-grid/topic/README.md b/modules/event-grid/topic/README.md index 8ae1c9ebdf..a00df258c6 100644 --- a/modules/event-grid/topic/README.md +++ b/modules/event-grid/topic/README.md @@ -33,6 +33,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) - [Pe](#example-3-pe) +- [WAF-aligned](#example-4-waf-aligned) ### Example 1: _Using only defaults_ @@ -377,6 +378,216 @@ module topic 'br:bicep/modules/event-grid.topic:1.0.0' = {
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module topic 'br:bicep/modules/event-grid.topic:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-egtwaf'
+ params: {
+ // Required parameters
+ name: 'egtwaf001'
+ // Non-required parameters
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "egtwaf001"
+ },
+ // Non-required parameters
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "
+ ## Parameters diff --git a/modules/event-grid/topic/tests/e2e/waf-aligned/dependencies.bicep b/modules/event-grid/topic/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..448380e27d --- /dev/null +++ b/modules/event-grid/topic/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,89 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +@description('Required. The name of the Storage Queue to create.') +param storageQueueName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.eventgrid.azure.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + + resource queueService 'queueServices@2022-09-01' = { + name: 'default' + + resource queue 'queues@2022-09-01' = { + name: storageQueueName + } + } +} + +@description('The name of the created Storage Account Queue.') +output queueName string = storageAccount::queueService::queue.name + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id + +@description('The resource ID of the created Storage Account.') +output storageAccountResourceId string = storageAccount.id diff --git a/modules/event-grid/topic/tests/e2e/waf-aligned/main.test.bicep b/modules/event-grid/topic/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..d093b9d5b8 --- /dev/null +++ b/modules/event-grid/topic/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,145 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-eventgrid.topics-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'egtwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + storageAccountName: 'dep${namePrefix}sa${serviceShort}' + storageQueueName: 'dep${namePrefix}sq${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + eventSubscriptions: [ { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + expirationTimeUtc: '2099-01-01T11:00:21.715Z' + filter: { + isSubjectCaseSensitive: false + enableAdvancedFilteringOnArrays: true + } + retryPolicy: { + maxDeliveryAttempts: 10 + eventTimeToLive: '120' + } + eventDeliverySchema: 'CloudEventSchemaV1_0' + destination: { + endpointType: 'StorageQueue' + properties: { + resourceId: nestedDependencies.outputs.storageAccountResourceId + queueMessageTimeToLiveInSeconds: 86400 + queueName: nestedDependencies.outputs.queueName + } + } + } ] + inboundIpRules: [ + { + action: 'Allow' + ipMask: '40.74.28.0/23' + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + service: 'topic' + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/event-hub/namespace/README.md b/modules/event-hub/namespace/README.md index 11384fca9e..c9fd2a30dd 100644 --- a/modules/event-hub/namespace/README.md +++ b/modules/event-hub/namespace/README.md @@ -39,6 +39,7 @@ The following section provides usage examples for the module, which were used to - [Encr](#example-2-encr) - [Using large parameter set](#example-3-using-large-parameter-set) - [Pe](#example-4-pe) +- [WAF-aligned](#example-5-waf-aligned) ### Example 1: _Using only defaults_ @@ -676,6 +677,402 @@ module namespace 'br:bicep/modules/event-hub.namespace:1.0.0' = {
+### Example 5: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module namespace 'br:bicep/modules/event-hub.namespace:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-ehnwaf'
+ params: {
+ // Required parameters
+ name: 'ehnwaf001'
+ // Non-required parameters
+ authorizationRules: [
+ {
+ name: 'RootManageSharedAccessKey'
+ rights: [
+ 'Listen'
+ 'Manage'
+ 'Send'
+ ]
+ }
+ {
+ name: 'SendListenAccess'
+ rights: [
+ 'Listen'
+ 'Send'
+ ]
+ }
+ ]
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "ehnwaf001"
+ },
+ // Non-required parameters
+ "authorizationRules": {
+ "value": [
+ {
+ "name": "RootManageSharedAccessKey",
+ "rights": [
+ "Listen",
+ "Manage",
+ "Send"
+ ]
+ },
+ {
+ "name": "SendListenAccess",
+ "rights": [
+ "Listen",
+ "Send"
+ ]
+ }
+ ]
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "
+ ## Parameters diff --git a/modules/event-hub/namespace/tests/e2e/waf-aligned/dependencies.bicep b/modules/event-hub/namespace/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..6bc7e40df9 --- /dev/null +++ b/modules/event-hub/namespace/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,83 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + serviceEndpoints: [ + { + service: 'Microsoft.EventHub' + } + ] + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.servicebus.windows.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id + +@description('The resource ID of the created Storage Account.') +output storageAccountResourceId string = storageAccount.id diff --git a/modules/event-hub/namespace/tests/e2e/waf-aligned/main.test.bicep b/modules/event-hub/namespace/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..53ec10b8b5 --- /dev/null +++ b/modules/event-hub/namespace/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,228 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-eventhub.namespaces-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'ehnwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + storageAccountName: 'dep${namePrefix}sa${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + zoneRedundant: true + skuName: 'Standard' + skuCapacity: 2 + authorizationRules: [ + { + name: 'RootManageSharedAccessKey' + rights: [ + 'Listen' + 'Manage' + 'Send' + ] + } + { + name: 'SendListenAccess' + rights: [ + 'Listen' + 'Send' + ] + } + ] + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + eventhubs: [ + { + name: '${namePrefix}-az-evh-x-001' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + } + { + name: '${namePrefix}-az-evh-x-002' + authorizationRules: [ + { + name: 'RootManageSharedAccessKey' + rights: [ + 'Listen' + 'Manage' + 'Send' + ] + } + { + name: 'SendListenAccess' + rights: [ + 'Listen' + 'Send' + ] + } + ] + captureDescriptionDestinationArchiveNameFormat: '{Namespace}/{EventHub}/{PartitionId}/{Year}/{Month}/{Day}/{Hour}/{Minute}/{Second}' + captureDescriptionDestinationBlobContainer: 'eventhub' + captureDescriptionDestinationName: 'EventHubArchive.AzureBlockBlob' + captureDescriptionDestinationStorageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId + captureDescriptionEnabled: true + captureDescriptionEncoding: 'Avro' + captureDescriptionIntervalInSeconds: 300 + captureDescriptionSizeLimitInBytes: 314572800 + captureDescriptionSkipEmptyArchives: true + consumergroups: [ + { + name: 'custom' + userMetadata: 'customMetadata' + } + ] + messageRetentionInDays: 1 + partitionCount: 2 + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + status: 'Active' + retentionDescriptionCleanupPolicy: 'Delete' + retentionDescriptionRetentionTimeInHours: 3 + } + { + name: '${namePrefix}-az-evh-x-003' + retentionDescriptionCleanupPolicy: 'Compact' + retentionDescriptionTombstoneRetentionTimeInHours: 24 + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + networkRuleSets: { + defaultAction: 'Deny' + ipRules: [ + { + action: 'Allow' + ipMask: '10.10.10.10' + } + ] + trustedServiceAccessEnabled: false + virtualNetworkRules: [ + { + ignoreMissingVnetServiceEndpoint: true + subnetResourceId: nestedDependencies.outputs.subnetResourceId + } + ] + } + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + service: 'namespace' + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + managedIdentities: { + systemAssigned: true + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + kafkaEnabled: true + disableLocalAuth: true + isAutoInflateEnabled: true + minimumTlsVersion: '1.2' + maximumThroughputUnits: 4 + publicNetworkAccess: 'Disabled' + } +} diff --git a/modules/health-bot/health-bot/README.md b/modules/health-bot/health-bot/README.md index 794c1f2f31..5d2aacf68b 100644 --- a/modules/health-bot/health-bot/README.md +++ b/modules/health-bot/health-bot/README.md @@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -183,6 +184,108 @@ module healthBot 'br:bicep/modules/health-bot.health-bot:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module healthBot 'br:bicep/modules/health-bot.health-bot:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-hbhbwaf'
+ params: {
+ // Required parameters
+ name: 'hbhbwaf001'
+ sku: 'F0'
+ // Non-required parameters
+ enableDefaultTelemetry: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "hbhbwaf001"
+ },
+ "sku": {
+ "value": "F0"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "
+ ## Parameters diff --git a/modules/health-bot/health-bot/tests/e2e/waf-aligned/dependencies.bicep b/modules/health-bot/health-bot/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..539240be2b --- /dev/null +++ b/modules/health-bot/health-bot/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,16 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/health-bot/health-bot/tests/e2e/waf-aligned/main.test.bicep b/modules/health-bot/health-bot/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..798f69c2f9 --- /dev/null +++ b/modules/health-bot/health-bot/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,78 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-healthbot.healthbots-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'hbhbwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + sku: 'F0' + managedIdentities: { + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + } +} diff --git a/modules/healthcare-apis/workspace/README.md b/modules/healthcare-apis/workspace/README.md index 75580c51f9..5c58fab11a 100644 --- a/modules/healthcare-apis/workspace/README.md +++ b/modules/healthcare-apis/workspace/README.md @@ -34,6 +34,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -373,6 +374,288 @@ module workspace 'br:bicep/modules/healthcare-apis.workspace:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module workspace 'br:bicep/modules/healthcare-apis.workspace:1.0.0' = {
+ name: '${uniqueString(deployment().name)}-test-hawwaf'
+ params: {
+ // Required parameters
+ name: 'hawwaf001'
+ // Non-required parameters
+ dicomservices: [
+ {
+ corsAllowCredentials: false
+ corsHeaders: [
+ '*'
+ ]
+ corsMaxAge: 600
+ corsMethods: [
+ 'GET'
+ ]
+ corsOrigins: [
+ '*'
+ ]
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "hawwaf001"
+ },
+ // Non-required parameters
+ "dicomservices": {
+ "value": [
+ {
+ "corsAllowCredentials": false,
+ "corsHeaders": [
+ "*"
+ ],
+ "corsMaxAge": 600,
+ "corsMethods": [
+ "GET"
+ ],
+ "corsOrigins": [
+ "*"
+ ],
+ "diagnosticSettings": [
+ {
+ "eventHubAuthorizationRuleResourceId": "
+ ## Parameters diff --git a/modules/healthcare-apis/workspace/tests/e2e/waf-aligned/dependencies.bicep b/modules/healthcare-apis/workspace/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..96f9aff771 --- /dev/null +++ b/modules/healthcare-apis/workspace/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,74 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Event Hub Namespace to create.') +param eventHubNamespaceName string + +@description('Required. The name of the Event Hub consumer group to create.') +param eventHubConsumerGroupName string + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' +} + +resource ehns 'Microsoft.EventHub/namespaces@2022-01-01-preview' = { + name: eventHubNamespaceName + location: location + sku: { + name: 'Standard' + tier: 'Standard' + capacity: 1 + } + properties: { + zoneRedundant: false + isAutoInflateEnabled: false + } + + resource eventhub 'eventhubs@2022-01-01-preview' = { + name: '${eventHubNamespaceName}-hub' + properties: { + messageRetentionInDays: 1 + partitionCount: 1 + } + + resource consumergroup 'consumergroups@2022-01-01-preview' = { + name: eventHubConsumerGroupName + } + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id +@description('The resource ID of the created Storage Account.') +output storageAccountResourceId string = storageAccount.id + +@description('The resource ID of the created Event Hub Namespace.') +output eventHubNamespaceResourceId string = ehns.id + +@description('The name of the created Event Hub Namespace.') +output eventHubNamespaceName string = ehns.name + +@description('The resource ID of the created Event Hub.') +output eventHubResourceId string = ehns::eventhub.id + +@description('The name of the created Event Hub.') +output eventHubName string = ehns::eventhub.name diff --git a/modules/healthcare-apis/workspace/tests/e2e/waf-aligned/main.test.bicep b/modules/healthcare-apis/workspace/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..bad448e7e7 --- /dev/null +++ b/modules/healthcare-apis/workspace/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,169 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-healthcareapis.workspaces-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'hawwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-paramNested' + params: { + eventHubConsumerGroupName: '${namePrefix}-az-iomt-x-001' + eventHubNamespaceName: 'dep-${namePrefix}-ehns-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + storageAccountName: 'dep${namePrefix}sa${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + location: location + publicNetworkAccess: 'Enabled' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + fhirservices: [ + { + name: '${namePrefix}-az-fhir-x-001' + kind: 'fhir-R4' + workspaceName: '${namePrefix}${serviceShort}001' + corsOrigins: [ '*' ] + corsHeaders: [ '*' ] + corsMethods: [ 'GET' ] + corsMaxAge: 600 + corsAllowCredentials: false + location: location + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + publicNetworkAccess: 'Enabled' + resourceVersionPolicy: 'versioned' + smartProxyEnabled: false + enableDefaultTelemetry: enableDefaultTelemetry + managedIdentities: { + systemAssigned: false + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + importEnabled: false + initialImportMode: false + roleAssignments: [ + { + roleDefinitionIdOrName: resourceId('Microsoft.Authorization/roleDefinitions', '5a1fc7df-4bf1-4951-a576-89034ee01acd') + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + } + ] + dicomservices: [ + { + name: '${namePrefix}-az-dicom-x-001' + workspaceName: '${namePrefix}${serviceShort}001' + corsOrigins: [ '*' ] + corsHeaders: [ '*' ] + corsMethods: [ 'GET' ] + corsMaxAge: 600 + corsAllowCredentials: false + location: location + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + publicNetworkAccess: 'Enabled' + enableDefaultTelemetry: enableDefaultTelemetry + managedIdentities: { + systemAssigned: false + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/insights/action-group/README.md b/modules/insights/action-group/README.md index d0edf08b29..36196c3663 100644 --- a/modules/insights/action-group/README.md +++ b/modules/insights/action-group/README.md @@ -30,6 +30,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -205,6 +206,128 @@ module actionGroup 'br:bicep/modules/insights.action-group:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module actionGroup 'br:bicep/modules/insights.action-group:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-iagwaf'
+ params: {
+ // Required parameters
+ groupShortName: 'agiagwaf001'
+ name: 'iagwaf001'
+ // Non-required parameters
+ emailReceivers: [
+ {
+ emailAddress: 'test.user@testcompany.com'
+ name: 'TestUser_-EmailAction-'
+ useCommonAlertSchema: true
+ }
+ {
+ emailAddress: 'test.user2@testcompany.com'
+ name: 'TestUser2'
+ useCommonAlertSchema: true
+ }
+ ]
+ enableDefaultTelemetry: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "groupShortName": {
+ "value": "agiagwaf001"
+ },
+ "name": {
+ "value": "iagwaf001"
+ },
+ // Non-required parameters
+ "emailReceivers": {
+ "value": [
+ {
+ "emailAddress": "test.user@testcompany.com",
+ "name": "TestUser_-EmailAction-",
+ "useCommonAlertSchema": true
+ },
+ {
+ "emailAddress": "test.user2@testcompany.com",
+ "name": "TestUser2",
+ "useCommonAlertSchema": true
+ }
+ ]
+ },
+ "enableDefaultTelemetry": {
+ "value": "
+ ## Parameters diff --git a/modules/insights/action-group/tests/e2e/waf-aligned/dependencies.bicep b/modules/insights/action-group/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..a7f42aee7b --- /dev/null +++ b/modules/insights/action-group/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/insights/action-group/tests/e2e/waf-aligned/main.test.bicep b/modules/insights/action-group/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..6059b1d2fd --- /dev/null +++ b/modules/insights/action-group/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,88 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-insights.actiongroups-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'iagwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + groupShortName: 'ag${serviceShort}001' + emailReceivers: [ + { + emailAddress: 'test.user@testcompany.com' + name: 'TestUser_-EmailAction-' + useCommonAlertSchema: true + } + { + emailAddress: 'test.user2@testcompany.com' + name: 'TestUser2' + useCommonAlertSchema: true + } + ] + smsReceivers: [ + { + countryCode: '1' + name: 'TestUser_-SMSAction-' + phoneNumber: '2345678901' + } + ] + roleAssignments: [ + { + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: 'Reader' + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/insights/activity-log-alert/README.md b/modules/insights/activity-log-alert/README.md index 5af0e285e5..09d6045d46 100644 --- a/modules/insights/activity-log-alert/README.md +++ b/modules/insights/activity-log-alert/README.md @@ -26,6 +26,7 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br:bicep/modules/insights.activity-log-alert:1.0.0`. - [Using large parameter set](#example-1-using-large-parameter-set) +- [WAF-aligned](#example-2-waf-aligned) ### Example 1: _Using large parameter set_ @@ -189,6 +190,168 @@ module activityLogAlert 'br:bicep/modules/insights.activity-log-alert:1.0.0' = {
+### Example 2: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module activityLogAlert 'br:bicep/modules/insights.activity-log-alert:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-ialawaf'
+ params: {
+ // Required parameters
+ conditions: [
+ {
+ equals: 'ServiceHealth'
+ field: 'category'
+ }
+ {
+ anyOf: [
+ {
+ equals: 'Incident'
+ field: 'properties.incidentType'
+ }
+ {
+ equals: 'Maintenance'
+ field: 'properties.incidentType'
+ }
+ ]
+ }
+ {
+ containsAny: [
+ 'Action Groups'
+ 'Activity Logs & Alerts'
+ ]
+ field: 'properties.impactedServices[*].ServiceName'
+ }
+ {
+ containsAny: [
+ 'Global'
+ 'West Europe'
+ ]
+ field: 'properties.impactedServices[*].ImpactedRegions[*].RegionName'
+ }
+ ]
+ name: 'ialawaf001'
+ // Non-required parameters
+ actions: [
+ {
+ actionGroupId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "conditions": {
+ "value": [
+ {
+ "equals": "ServiceHealth",
+ "field": "category"
+ },
+ {
+ "anyOf": [
+ {
+ "equals": "Incident",
+ "field": "properties.incidentType"
+ },
+ {
+ "equals": "Maintenance",
+ "field": "properties.incidentType"
+ }
+ ]
+ },
+ {
+ "containsAny": [
+ "Action Groups",
+ "Activity Logs & Alerts"
+ ],
+ "field": "properties.impactedServices[*].ServiceName"
+ },
+ {
+ "containsAny": [
+ "Global",
+ "West Europe"
+ ],
+ "field": "properties.impactedServices[*].ImpactedRegions[*].RegionName"
+ }
+ ]
+ },
+ "name": {
+ "value": "ialawaf001"
+ },
+ // Non-required parameters
+ "actions": {
+ "value": [
+ {
+ "actionGroupId": "
+ ## Parameters diff --git a/modules/insights/activity-log-alert/tests/e2e/waf-aligned/dependencies.bicep b/modules/insights/activity-log-alert/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..f031089363 --- /dev/null +++ b/modules/insights/activity-log-alert/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,28 @@ +@description('Required. The name of the Action Group to create.') +param actionGroupName string + +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource actionGroup 'Microsoft.Insights/actionGroups@2022-06-01' = { + name: actionGroupName + location: 'global' + properties: { + groupShortName: substring(replace(actionGroupName, '-', ''), 0, 11) + enabled: true + } +} + +@description('The resource ID of the created Action Group.') +output actionGroupResourceId string = actionGroup.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/insights/activity-log-alert/tests/e2e/waf-aligned/main.test.bicep b/modules/insights/activity-log-alert/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..e44bab24e9 --- /dev/null +++ b/modules/insights/activity-log-alert/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,109 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-insights.activityLogAlerts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'ialawaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + actionGroupName: 'dep-${namePrefix}-ag-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + conditions: [ + { + field: 'category' + equals: 'ServiceHealth' + } + { + anyOf: [ + { + field: 'properties.incidentType' + equals: 'Incident' + } + { + field: 'properties.incidentType' + equals: 'Maintenance' + } + ] + } + { + field: 'properties.impactedServices[*].ServiceName' + containsAny: [ + 'Action Groups' + 'Activity Logs & Alerts' + ] + } + { + field: 'properties.impactedServices[*].ImpactedRegions[*].RegionName' + containsAny: [ + 'West Europe' + 'Global' + ] + } + ] + actions: [ + { + actionGroupId: nestedDependencies.outputs.actionGroupResourceId + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + scopes: [ + subscription().id + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/insights/component/README.md b/modules/insights/component/README.md index 7bbf106053..d3ae5f6d37 100644 --- a/modules/insights/component/README.md +++ b/modules/insights/component/README.md @@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -191,6 +192,116 @@ module component 'br:bicep/modules/insights.component:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module component 'br:bicep/modules/insights.component:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-icwaf'
+ params: {
+ // Required parameters
+ name: 'icwaf001'
+ workspaceResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "icwaf001"
+ },
+ "workspaceResourceId": {
+ "value": "
+ ## Parameters diff --git a/modules/insights/component/tests/e2e/waf-aligned/dependencies.bicep b/modules/insights/component/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..a7f42aee7b --- /dev/null +++ b/modules/insights/component/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/insights/component/tests/e2e/waf-aligned/main.test.bicep b/modules/insights/component/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..19788dc94b --- /dev/null +++ b/modules/insights/component/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,97 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-insights.components-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'icwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/insights/data-collection-endpoint/README.md b/modules/insights/data-collection-endpoint/README.md index 9713158d2b..d6ae7ac41e 100644 --- a/modules/insights/data-collection-endpoint/README.md +++ b/modules/insights/data-collection-endpoint/README.md @@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -171,6 +172,100 @@ module dataCollectionEndpoint 'br:bicep/modules/insights.data-collection-endpoin
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module dataCollectionEndpoint 'br:bicep/modules/insights.data-collection-endpoint:1.0.0' = {
+ name: '${uniqueString(deployment().name)}-test-idcewaf'
+ params: {
+ // Required parameters
+ name: 'idcewaf001'
+ // Non-required parameters
+ enableDefaultTelemetry: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "idcewaf001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "
+ ## Parameters diff --git a/modules/insights/data-collection-endpoint/tests/e2e/waf-aligned/dependencies.bicep b/modules/insights/data-collection-endpoint/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..d16e1031b1 --- /dev/null +++ b/modules/insights/data-collection-endpoint/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the managed identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created managed identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/insights/data-collection-endpoint/tests/e2e/waf-aligned/main.test.bicep b/modules/insights/data-collection-endpoint/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..e587afcd6a --- /dev/null +++ b/modules/insights/data-collection-endpoint/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,74 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-insights.dataCollectionEndpoints-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'idcewaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module resourceGroupResources 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-paramNested' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + publicNetworkAccess: 'Enabled' + kind: 'Windows' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: resourceGroupResources.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + resourceType: 'Data Collection Rules' + kind: 'Windows' + } + } +} diff --git a/modules/insights/diagnostic-setting/README.md b/modules/insights/diagnostic-setting/README.md index a0353d69b1..acfb26a890 100644 --- a/modules/insights/diagnostic-setting/README.md +++ b/modules/insights/diagnostic-setting/README.md @@ -25,6 +25,7 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br:bicep/modules/insights.diagnostic-setting:1.0.0`. - [Using large parameter set](#example-1-using-large-parameter-set) +- [WAF-aligned](#example-2-waf-aligned) ### Example 1: _Using large parameter set_ @@ -98,6 +99,78 @@ module diagnosticSetting 'br:bicep/modules/insights.diagnostic-setting:1.0.0' =
+### Example 2: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module diagnosticSetting 'br:bicep/modules/insights.diagnostic-setting:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-idswaf'
+ params: {
+ enableDefaultTelemetry: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ "enableDefaultTelemetry": {
+ "value": "
+ ## Parameters diff --git a/modules/insights/diagnostic-setting/tests/e2e/waf-aligned/main.test.bicep b/modules/insights/diagnostic-setting/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..7836a24eed --- /dev/null +++ b/modules/insights/diagnostic-setting/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,70 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-insights.diagnosticsettings-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'idswaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } +} diff --git a/modules/insights/metric-alert/README.md b/modules/insights/metric-alert/README.md index a213c126aa..2a8c4ddd54 100644 --- a/modules/insights/metric-alert/README.md +++ b/modules/insights/metric-alert/README.md @@ -26,6 +26,7 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br:bicep/modules/insights.metric-alert:1.0.0`. - [Using large parameter set](#example-1-using-large-parameter-set) +- [WAF-aligned](#example-2-waf-aligned) ### Example 1: _Using large parameter set_ @@ -151,6 +152,130 @@ module metricAlert 'br:bicep/modules/insights.metric-alert:1.0.0' = {
+### Example 2: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module metricAlert 'br:bicep/modules/insights.metric-alert:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-imawaf'
+ params: {
+ // Required parameters
+ criterias: [
+ {
+ criterionType: 'StaticThresholdCriterion'
+ metricName: 'Percentage CPU'
+ metricNamespace: 'microsoft.compute/virtualmachines'
+ name: 'HighCPU'
+ operator: 'GreaterThan'
+ threshold: '90'
+ timeAggregation: 'Average'
+ }
+ ]
+ name: 'imawaf001'
+ // Non-required parameters
+ actions: [
+ '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "criterias": {
+ "value": [
+ {
+ "criterionType": "StaticThresholdCriterion",
+ "metricName": "Percentage CPU",
+ "metricNamespace": "microsoft.compute/virtualmachines",
+ "name": "HighCPU",
+ "operator": "GreaterThan",
+ "threshold": "90",
+ "timeAggregation": "Average"
+ }
+ ]
+ },
+ "name": {
+ "value": "imawaf001"
+ },
+ // Non-required parameters
+ "actions": {
+ "value": [
+ "
+ ## Parameters diff --git a/modules/insights/metric-alert/tests/e2e/waf-aligned/dependencies.bicep b/modules/insights/metric-alert/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..eb23eca835 --- /dev/null +++ b/modules/insights/metric-alert/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,29 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Action Group to create.') +param actionGroupName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource actionGroup 'Microsoft.Insights/actionGroups@2022-06-01' = { + name: actionGroupName + location: 'global' + + properties: { + enabled: true + groupShortName: substring(actionGroupName, 0, 11) + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Action Group.') +output actionGroupResourceId string = actionGroup.id diff --git a/modules/insights/metric-alert/tests/e2e/waf-aligned/main.test.bicep b/modules/insights/metric-alert/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..ee7bf8abbd --- /dev/null +++ b/modules/insights/metric-alert/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,87 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-insights.metricalerts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'imawaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + actionGroupName: 'dep-${namePrefix}-ag-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + criterias: [ + { + criterionType: 'StaticThresholdCriterion' + metricName: 'Percentage CPU' + metricNamespace: 'microsoft.compute/virtualmachines' + name: 'HighCPU' + operator: 'GreaterThan' + threshold: '90' + timeAggregation: 'Average' + } + ] + actions: [ + nestedDependencies.outputs.actionGroupResourceId + ] + alertCriteriaType: 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + targetResourceRegion: 'westeurope' + targetResourceType: 'microsoft.compute/virtualmachines' + windowSize: 'PT15M' + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/insights/private-link-scope/README.md b/modules/insights/private-link-scope/README.md index 57e6b05caa..847be38edc 100644 --- a/modules/insights/private-link-scope/README.md +++ b/modules/insights/private-link-scope/README.md @@ -31,6 +31,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -196,6 +197,123 @@ This instance deploys the module with most of its features enabled.
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+ name: '${uniqueString(deployment().name, location)}-test-iplswaf'
+ params: {
+ // Required parameters
+ name: 'iplswaf001'
+ // Non-required parameters
+ enableDefaultTelemetry: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "iplswaf001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "
+ ## Parameters diff --git a/modules/insights/private-link-scope/tests/e2e/waf-aligned/dependencies.bicep b/modules/insights/private-link-scope/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..e09c9b5a0c --- /dev/null +++ b/modules/insights/private-link-scope/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,71 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Log Analytics Workspace to create.') +param logAnalyticsWorkspaceName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.monitor.azure.com' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: logAnalyticsWorkspaceName + location: location +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id + +@description('The resource ID of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id diff --git a/modules/insights/private-link-scope/tests/e2e/waf-aligned/main.test.bicep b/modules/insights/private-link-scope/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..bda1d61e70 --- /dev/null +++ b/modules/insights/private-link-scope/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,89 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-insights.privatelinkscopes-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'iplswaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-la-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + scopedResources: [ + { + name: 'scoped1' + linkedResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/insights/scheduled-query-rule/README.md b/modules/insights/scheduled-query-rule/README.md index b84ede93c0..c243ee7cbb 100644 --- a/modules/insights/scheduled-query-rule/README.md +++ b/modules/insights/scheduled-query-rule/README.md @@ -26,6 +26,7 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br:bicep/modules/insights.scheduled-query-rule:1.0.0`. - [Using large parameter set](#example-1-using-large-parameter-set) +- [WAF-aligned](#example-2-waf-aligned) ### Example 1: _Using large parameter set_ @@ -191,6 +192,170 @@ module scheduledQueryRule 'br:bicep/modules/insights.scheduled-query-rule:1.0.0'
+### Example 2: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module scheduledQueryRule 'br:bicep/modules/insights.scheduled-query-rule:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-isqrwaf'
+ params: {
+ // Required parameters
+ criterias: {
+ allOf: [
+ {
+ dimensions: [
+ {
+ name: 'Computer'
+ operator: 'Include'
+ values: [
+ '*'
+ ]
+ }
+ {
+ name: 'InstanceName'
+ operator: 'Include'
+ values: [
+ '*'
+ ]
+ }
+ ]
+ metricMeasureColumn: 'AggregatedValue'
+ operator: 'GreaterThan'
+ query: 'Perf | where ObjectName == \'LogicalDisk\' | where CounterName == \'% Free Space\' | where InstanceName <> \'HarddiskVolume1\' and InstanceName <> \'_Total\' | summarize AggregatedValue = min(CounterValue) by Computer InstanceName bin(TimeGenerated5m)'
+ threshold: 0
+ timeAggregation: 'Average'
+ }
+ ]
+ }
+ name: 'isqrwaf001'
+ scopes: [
+ '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "criterias": {
+ "value": {
+ "allOf": [
+ {
+ "dimensions": [
+ {
+ "name": "Computer",
+ "operator": "Include",
+ "values": [
+ "*"
+ ]
+ },
+ {
+ "name": "InstanceName",
+ "operator": "Include",
+ "values": [
+ "*"
+ ]
+ }
+ ],
+ "metricMeasureColumn": "AggregatedValue",
+ "operator": "GreaterThan",
+ "query": "Perf | where ObjectName == \"LogicalDisk\" | where CounterName == \"% Free Space\" | where InstanceName <> \"HarddiskVolume1\" and InstanceName <> \"_Total\" | summarize AggregatedValue = min(CounterValue) by Computer, InstanceName, bin(TimeGenerated,5m)",
+ "threshold": 0,
+ "timeAggregation": "Average"
+ }
+ ]
+ }
+ },
+ "name": {
+ "value": "isqrwaf001"
+ },
+ "scopes": {
+ "value": [
+ "
+ ## Parameters diff --git a/modules/insights/scheduled-query-rule/tests/e2e/waf-aligned/dependencies.bicep b/modules/insights/scheduled-query-rule/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..9e9a8f2510 --- /dev/null +++ b/modules/insights/scheduled-query-rule/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,24 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Log Analytics Workspace to create.') +param logAnalyticsWorkspaceName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = { + name: logAnalyticsWorkspaceName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id diff --git a/modules/insights/scheduled-query-rule/tests/e2e/waf-aligned/main.test.bicep b/modules/insights/scheduled-query-rule/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..6c924009a5 --- /dev/null +++ b/modules/insights/scheduled-query-rule/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,105 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-insights.scheduledqueryrules-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'isqrwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + alertDescription: 'My sample Alert' + autoMitigate: false + criterias: { + allOf: [ + { + dimensions: [ + { + name: 'Computer' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'InstanceName' + operator: 'Include' + values: [ + '*' + ] + } + ] + metricMeasureColumn: 'AggregatedValue' + operator: 'GreaterThan' + query: 'Perf | where ObjectName == "LogicalDisk" | where CounterName == "% Free Space" | where InstanceName <> "HarddiskVolume1" and InstanceName <> "_Total" | summarize AggregatedValue = min(CounterValue) by Computer, InstanceName, bin(TimeGenerated,5m)' + threshold: 0 + timeAggregation: 'Average' + } + ] + } + evaluationFrequency: 'PT5M' + queryTimeRange: 'PT5M' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + scopes: [ + nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + ] + suppressForMinutes: 'PT5M' + windowSize: 'PT5M' + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/insights/webtest/README.md b/modules/insights/webtest/README.md index 9d2e805c8a..3f532543ca 100644 --- a/modules/insights/webtest/README.md +++ b/modules/insights/webtest/README.md @@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -199,6 +200,104 @@ module webtest 'br:bicep/modules/insights.webtest:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module webtest 'br:bicep/modules/insights.webtest:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-iwtwaf'
+ params: {
+ // Required parameters
+ name: 'iwtwaf001'
+ request: {
+ HttpVerb: 'GET'
+ RequestUrl: 'https://learn.microsoft.com/en-us/'
+ }
+ tags: {
+ 'hidden-link:${nestedDependencies.outputs.appInsightResourceId}': 'Resource'
+ 'hidden-title': 'This is visible in the resource name'
+ }
+ webTestName: 'wt$iwtwaf001'
+ // Non-required parameters
+ enableDefaultTelemetry: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "iwtwaf001"
+ },
+ "request": {
+ "value": {
+ "HttpVerb": "GET",
+ "RequestUrl": "https://learn.microsoft.com/en-us/"
+ }
+ },
+ "tags": {
+ "value": {
+ "hidden-link:${nestedDependencies.outputs.appInsightResourceId}": "Resource",
+ "hidden-title": "This is visible in the resource name"
+ }
+ },
+ "webTestName": {
+ "value": "wt$iwtwaf001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "
+ ## Parameters diff --git a/modules/insights/webtest/tests/e2e/waf-aligned/dependencies.bicep b/modules/insights/webtest/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..79e003515d --- /dev/null +++ b/modules/insights/webtest/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,26 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Log Analytics Workspace to create.') +param appInsightName string + +@description('Required. The name of the Log Analytics Workspace to create.') +param logAnalyticsWorkspaceName string + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = { + name: logAnalyticsWorkspaceName + location: location +} + +resource appInsight 'Microsoft.Insights/components@2020-02-02' = { + name: appInsightName + location: location + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspace.id + } +} + +@description('The resource ID of the created Log Analytics Workspace.') +output appInsightResourceId string = appInsight.id diff --git a/modules/insights/webtest/tests/e2e/waf-aligned/main.test.bicep b/modules/insights/webtest/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..0fcdae082d --- /dev/null +++ b/modules/insights/webtest/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,77 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-insights.webtests-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'iwtwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + appInsightName: 'dep-${namePrefix}-appi-${serviceShort}' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + tags: { + 'hidden-title': 'This is visible in the resource name' + 'hidden-link:${nestedDependencies.outputs.appInsightResourceId}': 'Resource' + } + enableDefaultTelemetry: enableDefaultTelemetry + webTestName: 'wt${namePrefix}$${serviceShort}001' + syntheticMonitorId: '${namePrefix}${serviceShort}001' + locations: [ + { + Id: 'emea-nl-ams-azr' + } + ] + request: { + RequestUrl: 'https://learn.microsoft.com/en-us/' + HttpVerb: 'GET' + } + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + } +} diff --git a/modules/key-vault/vault/README.md b/modules/key-vault/vault/README.md index 2072456778..155324660e 100644 --- a/modules/key-vault/vault/README.md +++ b/modules/key-vault/vault/README.md @@ -38,6 +38,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-2-using-only-defaults) - [Using large parameter set](#example-3-using-large-parameter-set) - [Pe](#example-4-pe) +- [WAF-aligned](#example-5-waf-aligned) ### Example 1: _Accesspolicies_ @@ -775,6 +776,308 @@ module vault 'br:bicep/modules/key-vault.vault:1.0.0' = {
+### Example 5: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module vault 'br:bicep/modules/key-vault.vault:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-kvvwaf'
+ params: {
+ // Required parameters
+ name: 'kvvwaf002'
+ // Non-required parameters
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ "name": {
+ "value": "kvvwaf002"
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "
+ ## Parameters diff --git a/modules/key-vault/vault/tests/e2e/waf-aligned/dependencies.bicep b/modules/key-vault/vault/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..6c3754d07f --- /dev/null +++ b/modules/key-vault/vault/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,65 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + serviceEndpoints: [ + { + service: 'Microsoft.KeyVault' + } + ] + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.vaultcore.azure.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id diff --git a/modules/key-vault/vault/tests/e2e/waf-aligned/main.test.bicep b/modules/key-vault/vault/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..edca2e6418 --- /dev/null +++ b/modules/key-vault/vault/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,189 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-keyvault.vaults-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'kvvwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}03' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}01' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}01' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}002' + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + // Only for testing purposes + enablePurgeProtection: false + enableRbacAuthorization: true + keys: [ + { + attributesExp: 1725109032 + attributesNbf: 10000 + name: 'keyName' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + rotationPolicy: { + attributes: { + expiryTime: 'P2Y' + } + lifetimeActions: [ + { + trigger: { + timeBeforeExpiry: 'P2M' + } + action: { + type: 'Rotate' + } + } + { + trigger: { + timeBeforeExpiry: 'P30D' + } + action: { + type: 'Notify' + } + } + ] + } + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + ipRules: [ + { + value: '40.74.28.0/23' + } + ] + virtualNetworkRules: [ + { + id: nestedDependencies.outputs.subnetResourceId + ignoreMissingVnetServiceEndpoint: false + } + ] + } + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + service: 'vault' + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + secrets: { + secureList: [ + { + attributesExp: 1702648632 + attributesNbf: 10000 + contentType: 'Something' + name: 'secretName' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + value: 'secretValue' + } + ] + } + softDeleteRetentionInDays: 7 + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/kubernetes-configuration/extension/README.md b/modules/kubernetes-configuration/extension/README.md index 34c51d8bc7..9019bb4998 100644 --- a/modules/kubernetes-configuration/extension/README.md +++ b/modules/kubernetes-configuration/extension/README.md @@ -29,6 +29,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -208,6 +209,120 @@ module extension 'br:bicep/modules/kubernetes-configuration.extension:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module extension 'br:bicep/modules/kubernetes-configuration.extension:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-kcewaf'
+ params: {
+ // Required parameters
+ clusterName: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "clusterName": {
+ "value": "
+ ## Parameters diff --git a/modules/kubernetes-configuration/extension/tests/e2e/waf-aligned/dependencies.bicep b/modules/kubernetes-configuration/extension/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..0169763539 --- /dev/null +++ b/modules/kubernetes-configuration/extension/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,32 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the AKS cluster to create.') +param clusterName string + +@description('Required. The name of the AKS cluster nodes resource group to create.') +param clusterNodeResourceGroupName string + +resource cluster 'Microsoft.ContainerService/managedClusters@2022-07-01' = { + name: clusterName + location: location + identity: { + type: 'SystemAssigned' + } + properties: { + dnsPrefix: clusterName + nodeResourceGroup: clusterNodeResourceGroupName + agentPoolProfiles: [ + { + name: 'agentpool' + count: 1 + vmSize: 'Standard_DS2_v2' + osType: 'Linux' + mode: 'System' + } + ] + } +} + +@description('The name of the created AKS cluster.') +output clusterName string = cluster.name diff --git a/modules/kubernetes-configuration/extension/tests/e2e/waf-aligned/main.test.bicep b/modules/kubernetes-configuration/extension/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..bfcc8c9102 --- /dev/null +++ b/modules/kubernetes-configuration/extension/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,84 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-kubernetesconfiguration.extensions-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'kcewaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + clusterName: 'dep-${namePrefix}-aks-${serviceShort}' + clusterNodeResourceGroupName: 'nodes-${resourceGroupName}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + clusterName: nestedDependencies.outputs.clusterName + extensionType: 'microsoft.flux' + configurationSettings: { + 'image-automation-controller.enabled': 'false' + 'image-reflector-controller.enabled': 'false' + 'kustomize-controller.enabled': 'true' + 'notification-controller.enabled': 'false' + 'source-controller.enabled': 'true' + } + releaseNamespace: 'flux-system' + releaseTrain: 'Stable' + version: '0.5.2' + fluxConfigurations: [ + { + namespace: 'flux-system' + scope: 'cluster' + gitRepository: { + repositoryRef: { + branch: 'main' + } + sshKnownHosts: '' + syncIntervalInSeconds: 300 + timeoutInSeconds: 180 + url: 'https://github.com/mspnp/aks-baseline' + } + } + ] + } +} diff --git a/modules/kubernetes-configuration/flux-configuration/README.md b/modules/kubernetes-configuration/flux-configuration/README.md index 22030b57cc..31ff175b92 100644 --- a/modules/kubernetes-configuration/flux-configuration/README.md +++ b/modules/kubernetes-configuration/flux-configuration/README.md @@ -29,6 +29,7 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -212,6 +213,108 @@ module fluxConfiguration 'br:bicep/modules/kubernetes-configuration.flux-configu
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module fluxConfiguration 'br:bicep/modules/kubernetes-configuration.flux-configuration:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-kcfcwaf'
+ params: {
+ // Required parameters
+ clusterName: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "clusterName": {
+ "value": "
+ ## Parameters diff --git a/modules/kubernetes-configuration/flux-configuration/tests/e2e/waf-aligned/dependencies.bicep b/modules/kubernetes-configuration/flux-configuration/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..0bf942bbd1 --- /dev/null +++ b/modules/kubernetes-configuration/flux-configuration/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,49 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the AKS cluster to create.') +param clusterName string + +@description('Required. The name of the AKS cluster extension to create.') +param clusterExtensionName string + +@description('Required. The name of the AKS cluster nodes resource group to create.') +param clusterNodeResourceGroupName string + +resource cluster 'Microsoft.ContainerService/managedClusters@2022-07-01' = { + name: clusterName + location: location + identity: { + type: 'SystemAssigned' + } + properties: { + dnsPrefix: clusterName + nodeResourceGroup: clusterNodeResourceGroupName + agentPoolProfiles: [ + { + name: 'agentpool' + count: 1 + vmSize: 'Standard_DS2_v2' + osType: 'Linux' + mode: 'System' + } + ] + } +} + +resource extension 'Microsoft.KubernetesConfiguration/extensions@2022-03-01' = { + scope: cluster + name: clusterExtensionName + properties: { + extensionType: 'microsoft.flux' + releaseTrain: 'Stable' + scope: { + cluster: { + releaseNamespace: 'flux-system' + } + } + } +} + +@description('The name of the created AKS cluster.') +output clusterName string = cluster.name diff --git a/modules/kubernetes-configuration/flux-configuration/tests/e2e/waf-aligned/main.test.bicep b/modules/kubernetes-configuration/flux-configuration/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..900a2585ff --- /dev/null +++ b/modules/kubernetes-configuration/flux-configuration/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,81 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-kubernetesconfiguration.fluxconfigurations-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'kcfcwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + clusterName: 'dep-${namePrefix}-aks-${serviceShort}' + clusterExtensionName: '${namePrefix}${serviceShort}001' + clusterNodeResourceGroupName: 'nodes-${resourceGroupName}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + clusterName: nestedDependencies.outputs.clusterName + namespace: 'flux-system' + scope: 'cluster' + sourceKind: 'GitRepository' + gitRepository: { + repositoryRef: { + branch: 'main' + } + sshKnownHosts: '' + syncIntervalInSeconds: 300 + timeoutInSeconds: 180 + url: 'https://github.com/mspnp/aks-baseline' + } + kustomizations: { + unified: { + dependsOn: [] + force: false + path: './cluster-manifests' + prune: true + syncIntervalInSeconds: 300 + timeoutInSeconds: 300 + } + } + } +} diff --git a/modules/logic/workflow/README.md b/modules/logic/workflow/README.md index ab3cbde145..9febb50863 100644 --- a/modules/logic/workflow/README.md +++ b/modules/logic/workflow/README.md @@ -29,6 +29,7 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br:bicep/modules/logic.workflow:1.0.0`. - [Using large parameter set](#example-1-using-large-parameter-set) +- [WAF-aligned](#example-2-waf-aligned) ### Example 1: _Using large parameter set_ @@ -224,6 +225,200 @@ module workflow 'br:bicep/modules/logic.workflow:1.0.0' = {
+### Example 2: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+via Bicep module
+
+```bicep
+module workflow 'br:bicep/modules/logic.workflow:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-lwwaf'
+ params: {
+ // Required parameters
+ name: 'lwwaf001'
+ // Non-required parameters
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: '
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "lwwaf001"
+ },
+ // Non-required parameters
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "
+
## Parameters
diff --git a/modules/logic/workflow/tests/e2e/waf-aligned/dependencies.bicep b/modules/logic/workflow/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..0f0755a6f4
--- /dev/null
+++ b/modules/logic/workflow/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,16 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
diff --git a/modules/logic/workflow/tests/e2e/waf-aligned/main.test.bicep b/modules/logic/workflow/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..d2a5747507
--- /dev/null
+++ b/modules/logic/workflow/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,136 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-logic.workflows-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'lwwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ managedIdentities: {
+ userAssignedResourcesIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ workflowActions: {
+ HTTP: {
+ inputs: {
+ body: {
+ BeginPeakTime: '
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/machine-learning-services/workspace/tests/e2e/waf-aligned/dependencies.bicep b/modules/machine-learning-services/workspace/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..4f7b46494d
--- /dev/null
+++ b/modules/machine-learning-services/workspace/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,134 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Key Vault to create.')
+param keyVaultName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the Application Insights instance to create.')
+param applicationInsightsName string
+
+@description('Required. The name of the Storage Account to create.')
+param storageAccountName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ }
+ }
+ ]
+ }
+}
+
+resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
+ name: keyVaultName
+ location: location
+ properties: {
+ sku: {
+ family: 'A'
+ name: 'standard'
+ }
+ tenantId: tenant().tenantId
+ enablePurgeProtection: null
+ enabledForTemplateDeployment: true
+ enabledForDiskEncryption: true
+ enabledForDeployment: true
+ enableRbacAuthorization: true
+ accessPolicies: []
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource keyVaultServicePermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
+ name: guid('msi-${keyVault.id}-${location}-${managedIdentity.id}-KeyVault-Contributor-RoleAssignment')
+ scope: keyVault
+ properties: {
+ principalId: managedIdentity.properties.principalId
+ roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') // Contributor
+ principalType: 'ServicePrincipal'
+ }
+}
+resource keyVaultDataPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
+ name: guid('msi-${keyVault.id}-${location}-${managedIdentity.id}-KeyVault-Data-Admin-RoleAssignment')
+ scope: keyVault
+ properties: {
+ principalId: managedIdentity.properties.principalId
+ roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') // Key Vault Administrator
+ principalType: 'ServicePrincipal'
+ }
+}
+
+resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {
+ name: applicationInsightsName
+ location: location
+ kind: ''
+ properties: {}
+}
+
+resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = {
+ name: storageAccountName
+ location: location
+ sku: {
+ name: 'Standard_LRS'
+ }
+ kind: 'StorageV2'
+}
+
+resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
+ name: 'privatelink.api.azureml.ms'
+ location: 'global'
+
+ resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = {
+ name: '${virtualNetwork.name}-vnetlink'
+ location: 'global'
+ properties: {
+ virtualNetwork: {
+ id: virtualNetwork.id
+ }
+ registrationEnabled: false
+ }
+ }
+}
+
+@description('The resource ID of the created Virtual Network Subnet.')
+output subnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The resource ID of the created Key Vault.')
+output keyVaultResourceId string = keyVault.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
+
+@description('The resource ID of the created Application Insights instance.')
+output applicationInsightsResourceId string = applicationInsights.id
+
+@description('The resource ID of the created Storage Account.')
+output storageAccountResourceId string = storageAccount.id
+
+@description('The resource ID of the created Private DNS Zone.')
+output privateDNSZoneResourceId string = privateDNSZone.id
diff --git a/modules/machine-learning-services/workspace/tests/e2e/waf-aligned/main.test.bicep b/modules/machine-learning-services/workspace/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..2c0000e5e5
--- /dev/null
+++ b/modules/machine-learning-services/workspace/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,162 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-machinelearningservices.workspaces-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'mlswwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}'
+ applicationInsightsName: 'dep-${namePrefix}-appi-${serviceShort}'
+ storageAccountName: 'dep${namePrefix}st${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ associatedApplicationInsightsResourceId: nestedDependencies.outputs.applicationInsightsResourceId
+ associatedKeyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId
+ associatedStorageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId
+ sku: 'Premium'
+ computes: [
+ {
+ computeLocation: 'westeurope'
+ computeType: 'AmlCompute'
+ description: 'Default CPU Cluster'
+ disableLocalAuth: false
+ location: 'westeurope'
+ name: 'DefaultCPU'
+ properties: {
+ enableNodePublicIp: true
+ isolatedNetwork: false
+ osType: 'Linux'
+ remoteLoginPortPublicAccess: 'Disabled'
+ scaleSettings: {
+ maxNodeCount: 3
+ minNodeCount: 0
+ nodeIdleTimeBeforeScaleDown: 'PT5M'
+ }
+ vmPriority: 'Dedicated'
+ vmSize: 'STANDARD_DS11_V2'
+ }
+ sku: 'Basic'
+ // Must be false if `primaryUserAssignedIdentity` is provided
+ managedIdentities: {
+ systemAssigned: false
+ userAssignedResourcesIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ }
+ ]
+ description: 'The cake is a lie.'
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ discoveryUrl: 'http://example.com'
+ imageBuildCompute: 'testcompute'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ primaryUserAssignedIdentity: nestedDependencies.outputs.managedIdentityResourceId
+ privateEndpoints: [
+ {
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.privateDNSZoneResourceId
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ managedIdentities: {
+ systemAssigned: false
+ userAssignedResourcesIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/maintenance/maintenance-configuration/README.md b/modules/maintenance/maintenance-configuration/README.md
index 187dac5dc9..208ba523f4 100644
--- a/modules/maintenance/maintenance-configuration/README.md
+++ b/modules/maintenance/maintenance-configuration/README.md
@@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -229,6 +230,158 @@ module maintenanceConfiguration 'br:bicep/modules/maintenance.maintenance-config
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/maintenance/maintenance-configuration/tests/e2e/waf-aligned/dependencies.bicep b/modules/maintenance/maintenance-configuration/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/maintenance/maintenance-configuration/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/maintenance/maintenance-configuration/tests/e2e/waf-aligned/main.test.bicep b/modules/maintenance/maintenance-configuration/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..467bb46bba
--- /dev/null
+++ b/modules/maintenance/maintenance-configuration/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,101 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-maintenance.maintenanceconfigurations-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'mmcwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ extensionProperties: {
+ InGuestPatchMode: 'User'
+ }
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ maintenanceScope: 'InGuestPatch'
+ maintenanceWindow: {
+ duration: '03:00'
+ expirationDateTime: '9999-12-31 23:59:59'
+ recurEvery: 'Day'
+ startDateTime: '2022-12-31 13:00'
+ timeZone: 'W. Europe Standard Time'
+ }
+ namespace: '${serviceShort}ns'
+ visibility: 'Custom'
+ installPatches: {
+ linuxParameters: {
+ classificationsToInclude: null
+ packageNameMasksToExclude: null
+ packageNameMasksToInclude: null
+ }
+ rebootSetting: 'IfRequired'
+ windowsParameters: {
+ classificationsToInclude: [
+ 'Critical'
+ 'Security'
+ ]
+ kbNumbersToExclude: null
+ kbNumbersToInclude: null
+ }
+ }
+ }
+}
diff --git a/modules/managed-identity/user-assigned-identity/README.md b/modules/managed-identity/user-assigned-identity/README.md
index d76e767ebe..c2e921ae09 100644
--- a/modules/managed-identity/user-assigned-identity/README.md
+++ b/modules/managed-identity/user-assigned-identity/README.md
@@ -29,6 +29,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -174,6 +175,110 @@ module userAssignedIdentity 'br:bicep/modules/managed-identity.user-assigned-ide
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/managed-identity/user-assigned-identity/tests/e2e/waf-aligned/dependencies.bicep b/modules/managed-identity/user-assigned-identity/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/managed-identity/user-assigned-identity/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/managed-identity/user-assigned-identity/tests/e2e/waf-aligned/main.test.bicep b/modules/managed-identity/user-assigned-identity/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..fababf8321
--- /dev/null
+++ b/modules/managed-identity/user-assigned-identity/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,82 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-managedidentity.userassignedidentities-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'miuaiwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ federatedIdentityCredentials: [
+ {
+ name: 'test-fed-cred-${serviceShort}-001'
+ audiences: [
+ 'api://AzureADTokenExchange'
+ ]
+ issuer: 'https://contoso.com/${subscription().tenantId}/${guid(deployment().name)}/'
+ subject: 'system:serviceaccount:default:workload-identity-sa'
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/managed-services/registration-definition/README.md b/modules/managed-services/registration-definition/README.md
index 472774ac03..759632f268 100644
--- a/modules/managed-services/registration-definition/README.md
+++ b/modules/managed-services/registration-definition/README.md
@@ -32,6 +32,7 @@ The following section provides usage examples for the module, which were used to
- [Using large parameter set](#example-1-using-large-parameter-set)
- [Rg](#example-2-rg)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using large parameter set_
@@ -218,6 +219,98 @@ module registrationDefinition 'br:bicep/modules/managed-services.registration-de
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/managed-services/registration-definition/tests/e2e/waf-aligned/main.test.bicep b/modules/managed-services/registration-definition/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..553e1b72b9
--- /dev/null
+++ b/modules/managed-services/registration-definition/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,48 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'msrdwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ name: '${uniqueString(deployment().name)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: 'Component Validation - ${namePrefix}${serviceShort} Subscription assignment'
+ authorizations: [
+ {
+ principalId: '<< SET YOUR PRINCIPAL ID 1 HERE >>'
+ principalIdDisplayName: 'ResourceModules-Reader'
+ roleDefinitionId: 'acdd72a7-3385-48ef-bd42-f606fba81ae7'
+ }
+ {
+ principalId: '<< SET YOUR PRINCIPAL ID 2 HERE >>'
+ principalIdDisplayName: 'ResourceModules-Contributor'
+ roleDefinitionId: 'b24988ac-6180-42a0-ab88-20f7382dd24c'
+ }
+ {
+ principalId: '<< SET YOUR PRINCIPAL ID 3 HERE >>'
+ principalIdDisplayName: 'ResourceModules-LHManagement'
+ roleDefinitionId: '91c1777a-f3dc-4fae-b103-61d183457e46'
+ }
+ ]
+ managedByTenantId: '<< SET YOUR TENANT ID HERE >>'
+ registrationDescription: 'Managed by Lighthouse'
+ }
+}
diff --git a/modules/management/management-group/README.md b/modules/management/management-group/README.md
index 38c1b4d408..d5e7a66097 100644
--- a/modules/management/management-group/README.md
+++ b/modules/management/management-group/README.md
@@ -31,6 +31,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -136,6 +137,62 @@ module managementGroup 'br:bicep/modules/management.management-group:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/management/management-group/tests/e2e/waf-aligned/main.test.bicep b/modules/management/management-group/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..8ccb083802
--- /dev/null
+++ b/modules/management/management-group/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,31 @@
+targetScope = 'managementGroup'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'mmgwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ name: '${uniqueString(deployment().name)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ displayName: 'Test MG'
+ parentId: last(split(managementGroup().id, '/'))
+ }
+}
diff --git a/modules/network/application-gateway-web-application-firewall-policy/README.md b/modules/network/application-gateway-web-application-firewall-policy/README.md
index feb78de452..9b9ea51250 100644
--- a/modules/network/application-gateway-web-application-firewall-policy/README.md
+++ b/modules/network/application-gateway-web-application-firewall-policy/README.md
@@ -25,6 +25,7 @@ The following section provides usage examples for the module, which were used to
>**Note**: To reference the module, please use the following syntax `br:bicep/modules/network.application-gateway-web-application-firewall-policy:1.0.0`.
- [Using large parameter set](#example-1-using-large-parameter-set)
+- [WAF-aligned](#example-2-waf-aligned)
### Example 1: _Using large parameter set_
@@ -128,6 +129,108 @@ module applicationGatewayWebApplicationFirewallPolicy 'br:bicep/modules/network.
+### Example 2: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/application-gateway-web-application-firewall-policy/tests/e2e/waf-aligned/main.test.bicep b/modules/network/application-gateway-web-application-firewall-policy/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..0629a475af
--- /dev/null
+++ b/modules/network/application-gateway-web-application-firewall-policy/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,72 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.applicationGatewayWebApplicationFirewallPolicies-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nagwafpwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ policySettings: {
+ fileUploadLimitInMb: 10
+ state: 'Enabled'
+ mode: 'Prevention'
+ }
+ managedRules: {
+ managedRuleSets: [
+ {
+ ruleSetType: 'OWASP'
+ ruleSetVersion: '3.2'
+ ruleGroupOverrides: []
+ }
+ {
+ ruleSetType: 'Microsoft_BotManagerRuleSet'
+ ruleSetVersion: '0.1'
+ ruleGroupOverrides: []
+ }
+ ]
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/application-gateway/README.md b/modules/network/application-gateway/README.md
index 853769d2f6..0a7a5b8a1f 100644
--- a/modules/network/application-gateway/README.md
+++ b/modules/network/application-gateway/README.md
@@ -30,6 +30,7 @@ The following section provides usage examples for the module, which were used to
>**Note**: To reference the module, please use the following syntax `br:bicep/modules/network.application-gateway:1.0.0`.
- [Using large parameter set](#example-1-using-large-parameter-set)
+- [WAF-aligned](#example-2-waf-aligned)
### Example 1: _Using large parameter set_
@@ -965,6 +966,940 @@ module applicationGateway 'br:bicep/modules/network.application-gateway:1.0.0' =
+### Example 2: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/application-gateway/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/application-gateway/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..2de1a81653
--- /dev/null
+++ b/modules/network/application-gateway/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,146 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Public IP to create.')
+param publicIPName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the Key Vault to create.')
+param keyVaultName string
+
+@description('Required. The name of the Deployment Script to create for the Certificate generation.')
+param certDeploymentScriptName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 24, 0)
+ }
+ }
+ {
+ name: 'privateLinkSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 24, 1)
+ privateLinkServiceNetworkPolicies: 'Disabled'
+ }
+ }
+ ]
+ }
+}
+
+resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
+ name: 'privatelink.appgateway.net'
+ location: 'global'
+
+ resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = {
+ name: '${virtualNetwork.name}-vnetlink'
+ location: 'global'
+ properties: {
+ virtualNetwork: {
+ id: virtualNetwork.id
+ }
+ registrationEnabled: false
+ }
+ }
+}
+
+resource publicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = {
+ name: publicIPName
+ location: location
+ sku: {
+ name: 'Standard'
+ tier: 'Regional'
+ }
+ properties: {
+ publicIPAllocationMethod: 'Static'
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
+ name: keyVaultName
+ location: location
+ properties: {
+ sku: {
+ family: 'A'
+ name: 'standard'
+ }
+ tenantId: tenant().tenantId
+ enablePurgeProtection: null
+ enabledForTemplateDeployment: true
+ enabledForDiskEncryption: true
+ enabledForDeployment: true
+ enableRbacAuthorization: true
+ accessPolicies: []
+ }
+}
+
+resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
+ name: guid('msi-${managedIdentity.name}-KeyVault-Admin-RoleAssignment')
+ scope: keyVault
+ properties: {
+ principalId: managedIdentity.properties.principalId
+ roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') // Key Vault Administrator
+ principalType: 'ServicePrincipal'
+ }
+}
+
+resource certDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
+ name: certDeploymentScriptName
+ location: location
+ kind: 'AzurePowerShell'
+ identity: {
+ type: 'UserAssigned'
+ userAssignedIdentities: {
+ '${managedIdentity.id}': {}
+ }
+ }
+ properties: {
+ azPowerShellVersion: '8.0'
+ retentionInterval: 'P1D'
+ arguments: '-KeyVaultName "${keyVault.name}" -CertName "applicationGatewaySslCertificate"'
+ scriptContent: loadTextContent('../../../../../.shared/.scripts/Set-CertificateInKeyVault.ps1')
+ }
+}
+
+@description('The resource ID of the created Virtual Network default subnet.')
+output defaultSubnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The resource ID of the created Virtual Network private link subnet.')
+output privateLinkSubnetResourceId string = virtualNetwork.properties.subnets[1].id
+
+@description('The resource ID of the created Public IP.')
+output publicIPResourceId string = publicIP.id
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
+
+@description('The URL of the created certificate.')
+output certificateSecretUrl string = certDeploymentScript.properties.outputs.secretUrl
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Private DNS Zone.')
+output privateDNSZoneResourceId string = privateDNSZone.id
diff --git a/modules/network/application-gateway/tests/e2e/waf-aligned/main.test.bicep b/modules/network/application-gateway/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..43b1c3d630
--- /dev/null
+++ b/modules/network/application-gateway/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,498 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.applicationgateways-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nagwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ publicIPName: 'dep-${namePrefix}-pip-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ certDeploymentScriptName: 'dep-${namePrefix}-ds-${serviceShort}'
+ keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+var appGWName = '${namePrefix}${serviceShort}001'
+var appGWExpectedResourceID = '${resourceGroup.id}/providers/Microsoft.Network/applicationGateways/${appGWName}'
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: appGWName
+ backendAddressPools: [
+ {
+ name: 'appServiceBackendPool'
+ properties: {
+ backendAddresses: [
+ {
+ fqdn: 'aghapp.azurewebsites.net'
+ }
+ ]
+ }
+ }
+ {
+ name: 'privateVmBackendPool'
+ properties: {
+ backendAddresses: [
+ {
+ ipAddress: '10.0.0.4'
+ }
+ ]
+ }
+ }
+ ]
+ backendHttpSettingsCollection: [
+ {
+ name: 'appServiceBackendHttpsSetting'
+ properties: {
+ cookieBasedAffinity: 'Disabled'
+ pickHostNameFromBackendAddress: true
+ port: 443
+ protocol: 'Https'
+ requestTimeout: 30
+ }
+ }
+ {
+ name: 'privateVmHttpSetting'
+ properties: {
+ cookieBasedAffinity: 'Disabled'
+ pickHostNameFromBackendAddress: false
+ port: 80
+ probe: {
+ id: '${appGWExpectedResourceID}/probes/privateVmHttpSettingProbe'
+ }
+ protocol: 'Http'
+ requestTimeout: 30
+ }
+ }
+ ]
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ enableHttp2: true
+ privateLinkConfigurations: [
+ {
+ name: 'pvtlink01'
+ id: '${appGWExpectedResourceID}/privateLinkConfigurations/pvtlink01'
+ properties: {
+ ipConfigurations: [
+ {
+ name: 'privateLinkIpConfig1'
+ id: '${appGWExpectedResourceID}/privateLinkConfigurations/pvtlink01/ipConfigurations/privateLinkIpConfig1'
+ properties: {
+ privateIPAllocationMethod: 'Dynamic'
+ primary: false
+ subnet: {
+ id: nestedDependencies.outputs.privateLinkSubnetResourceId
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+ privateEndpoints: [
+ {
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.privateDNSZoneResourceId
+ ]
+ service: 'public'
+ subnetResourceId: nestedDependencies.outputs.privateLinkSubnetResourceId
+ tags: {
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ frontendIPConfigurations: [
+ {
+ name: 'private'
+ properties: {
+ privateIPAddress: '10.0.0.20'
+ privateIPAllocationMethod: 'Static'
+ subnet: {
+ id: nestedDependencies.outputs.defaultSubnetResourceId
+ }
+ }
+ }
+ {
+ name: 'public'
+ properties: {
+ privateIPAllocationMethod: 'Dynamic'
+ publicIPAddress: {
+ id: nestedDependencies.outputs.publicIPResourceId
+ }
+ privateLinkConfiguration: {
+ id: '${appGWExpectedResourceID}/privateLinkConfigurations/pvtlink01'
+ }
+ }
+ }
+ ]
+ frontendPorts: [
+ {
+ name: 'port443'
+ properties: {
+ port: 443
+ }
+ }
+ {
+ name: 'port4433'
+ properties: {
+ port: 4433
+ }
+ }
+ {
+ name: 'port80'
+ properties: {
+ port: 80
+ }
+ }
+ {
+ name: 'port8080'
+ properties: {
+ port: 8080
+ }
+ }
+ ]
+ gatewayIPConfigurations: [
+ {
+ name: 'apw-ip-configuration'
+ properties: {
+ subnet: {
+ id: nestedDependencies.outputs.defaultSubnetResourceId
+ }
+ }
+ }
+ ]
+ httpListeners: [
+ {
+ name: 'public443'
+ properties: {
+ frontendIPConfiguration: {
+ id: '${appGWExpectedResourceID}/frontendIPConfigurations/public'
+ }
+ frontendPort: {
+ id: '${appGWExpectedResourceID}/frontendPorts/port443'
+ }
+ hostNames: []
+ protocol: 'https'
+ requireServerNameIndication: false
+ sslCertificate: {
+ id: '${appGWExpectedResourceID}/sslCertificates/${namePrefix}-az-apgw-x-001-ssl-certificate'
+ }
+ }
+ }
+ {
+ name: 'private4433'
+ properties: {
+ frontendIPConfiguration: {
+ id: '${appGWExpectedResourceID}/frontendIPConfigurations/private'
+ }
+ frontendPort: {
+ id: '${appGWExpectedResourceID}/frontendPorts/port4433'
+ }
+ hostNames: []
+ protocol: 'https'
+ requireServerNameIndication: false
+ sslCertificate: {
+ id: '${appGWExpectedResourceID}/sslCertificates/${namePrefix}-az-apgw-x-001-ssl-certificate'
+ }
+ }
+ }
+ {
+ name: 'httpRedirect80'
+ properties: {
+ frontendIPConfiguration: {
+ id: '${appGWExpectedResourceID}/frontendIPConfigurations/public'
+ }
+ frontendPort: {
+ id: '${appGWExpectedResourceID}/frontendPorts/port80'
+ }
+ hostNames: []
+ protocol: 'Http'
+ requireServerNameIndication: false
+ }
+ }
+ {
+ name: 'httpRedirect8080'
+ properties: {
+ frontendIPConfiguration: {
+ id: '${appGWExpectedResourceID}/frontendIPConfigurations/private'
+ }
+ frontendPort: {
+ id: '${appGWExpectedResourceID}/frontendPorts/port8080'
+ }
+ hostNames: []
+ protocol: 'Http'
+ requireServerNameIndication: false
+ }
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ probes: [
+ {
+ name: 'privateVmHttpSettingProbe'
+ properties: {
+ host: '10.0.0.4'
+ interval: 60
+ match: {
+ statusCodes: [
+ '200'
+ '401'
+ ]
+ }
+ minServers: 3
+ path: '/'
+ pickHostNameFromBackendHttpSettings: false
+ protocol: 'Http'
+ timeout: 15
+ unhealthyThreshold: 5
+ }
+ }
+ ]
+ redirectConfigurations: [
+ {
+ name: 'httpRedirect80'
+ properties: {
+ includePath: true
+ includeQueryString: true
+ redirectType: 'Permanent'
+ requestRoutingRules: [
+ {
+ id: '${appGWExpectedResourceID}/requestRoutingRules/httpRedirect80-public443'
+ }
+ ]
+ targetListener: {
+ id: '${appGWExpectedResourceID}/httpListeners/public443'
+ }
+ }
+ }
+ {
+ name: 'httpRedirect8080'
+ properties: {
+ includePath: true
+ includeQueryString: true
+ redirectType: 'Permanent'
+ requestRoutingRules: [
+ {
+ id: '${appGWExpectedResourceID}/requestRoutingRules/httpRedirect8080-private4433'
+ }
+ ]
+ targetListener: {
+ id: '${appGWExpectedResourceID}/httpListeners/private4433'
+ }
+ }
+ }
+ ]
+ requestRoutingRules: [
+ {
+ name: 'public443-appServiceBackendHttpsSetting-appServiceBackendHttpsSetting'
+ properties: {
+ backendAddressPool: {
+ id: '${appGWExpectedResourceID}/backendAddressPools/appServiceBackendPool'
+ }
+ backendHttpSettings: {
+ id: '${appGWExpectedResourceID}/backendHttpSettingsCollection/appServiceBackendHttpsSetting'
+ }
+ httpListener: {
+ id: '${appGWExpectedResourceID}/httpListeners/public443'
+ }
+ priority: 200
+ ruleType: 'Basic'
+ }
+ }
+ {
+ name: 'private4433-privateVmHttpSetting-privateVmHttpSetting'
+ properties: {
+ backendAddressPool: {
+ id: '${appGWExpectedResourceID}/backendAddressPools/privateVmBackendPool'
+ }
+ backendHttpSettings: {
+ id: '${appGWExpectedResourceID}/backendHttpSettingsCollection/privateVmHttpSetting'
+ }
+ httpListener: {
+ id: '${appGWExpectedResourceID}/httpListeners/private4433'
+ }
+ priority: 250
+ ruleType: 'Basic'
+ }
+ }
+ {
+ name: 'httpRedirect80-public443'
+ properties: {
+ httpListener: {
+ id: '${appGWExpectedResourceID}/httpListeners/httpRedirect80'
+ }
+ priority: 300
+ redirectConfiguration: {
+ id: '${appGWExpectedResourceID}/redirectConfigurations/httpRedirect80'
+ }
+ ruleType: 'Basic'
+ }
+ }
+ {
+ name: 'httpRedirect8080-private4433'
+ properties: {
+ httpListener: {
+ id: '${appGWExpectedResourceID}/httpListeners/httpRedirect8080'
+ }
+ priority: 350
+ redirectConfiguration: {
+ id: '${appGWExpectedResourceID}/redirectConfigurations/httpRedirect8080'
+ }
+ ruleType: 'Basic'
+ rewriteRuleSet: {
+ id: '${appGWExpectedResourceID}/rewriteRuleSets/customRewrite'
+ }
+ }
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ sku: 'WAF_v2'
+ sslCertificates: [
+ {
+ name: '${namePrefix}-az-apgw-x-001-ssl-certificate'
+ properties: {
+ keyVaultSecretId: nestedDependencies.outputs.certificateSecretUrl
+ }
+ }
+ ]
+ managedIdentities: {
+ userAssignedResourcesIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ rewriteRuleSets: [
+ {
+ name: 'customRewrite'
+ id: '${appGWExpectedResourceID}/rewriteRuleSets/customRewrite'
+ properties: {
+ rewriteRules: [
+ {
+ ruleSequence: 100
+ conditions: []
+ name: 'NewRewrite'
+ actionSet: {
+ requestHeaderConfigurations: [
+ {
+ headerName: 'Content-Type'
+ headerValue: 'JSON'
+ }
+ {
+ headerName: 'someheader'
+ }
+ ]
+ responseHeaderConfigurations: []
+ }
+ }
+ ]
+ }
+ }
+ ]
+ webApplicationFirewallConfiguration: {
+ enabled: true
+ fileUploadLimitInMb: 100
+ firewallMode: 'Detection'
+ maxRequestBodySizeInKb: 128
+ requestBodyCheck: true
+ ruleSetType: 'OWASP'
+ ruleSetVersion: '3.0'
+ disabledRuleGroups: [
+ {
+ ruleGroupName: 'Known-CVEs'
+ }
+ {
+ ruleGroupName: 'REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION'
+ }
+ {
+ ruleGroupName: 'REQUEST-941-APPLICATION-ATTACK-XSS'
+ }
+ ]
+ exclusions: [
+ {
+ matchVariable: 'RequestHeaderNames'
+ selectorMatchOperator: 'StartsWith'
+ selector: 'hola'
+ }
+ ]
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/application-security-group/README.md b/modules/network/application-security-group/README.md
index 37e573fa66..362a0f108d 100644
--- a/modules/network/application-security-group/README.md
+++ b/modules/network/application-security-group/README.md
@@ -27,6 +27,7 @@ The following section provides usage examples for the module, which were used to
>**Note**: To reference the module, please use the following syntax `br:bicep/modules/network.application-security-group:1.0.0`.
- [Using large parameter set](#example-1-using-large-parameter-set)
+- [WAF-aligned](#example-2-waf-aligned)
### Example 1: _Using large parameter set_
@@ -114,6 +115,92 @@ module applicationSecurityGroup 'br:bicep/modules/network.application-security-g
+### Example 2: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/application-security-group/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/application-security-group/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/network/application-security-group/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/application-security-group/tests/e2e/waf-aligned/main.test.bicep b/modules/network/application-security-group/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..052a71f7b1
--- /dev/null
+++ b/modules/network/application-security-group/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,72 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.applicationsecuritygroups-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nasgwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/azure-firewall/README.md b/modules/network/azure-firewall/README.md
index 2f41e39161..1a29630003 100644
--- a/modules/network/azure-firewall/README.md
+++ b/modules/network/azure-firewall/README.md
@@ -34,6 +34,7 @@ The following section provides usage examples for the module, which were used to
- [Hubcommon](#example-4-hubcommon)
- [Hubmin](#example-5-hubmin)
- [Using large parameter set](#example-6-using-large-parameter-set)
+- [WAF-aligned](#example-7-waf-aligned)
### Example 1: _Addpip_
@@ -747,6 +748,308 @@ module azureFirewall 'br:bicep/modules/network.azure-firewall:1.0.0' = {
+### Example 7: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/azure-firewall/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/azure-firewall/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..de9bfec4ea
--- /dev/null
+++ b/modules/network/azure-firewall/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,64 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Public IP to create.')
+param publicIPName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'AzureFirewallSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ }
+ }
+ ]
+ }
+}
+
+resource publicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = {
+ name: publicIPName
+ location: location
+ sku: {
+ name: 'Standard'
+ tier: 'Regional'
+ }
+ properties: {
+ publicIPAllocationMethod: 'Static'
+ }
+ zones: [
+ '1'
+ '2'
+ '3'
+ ]
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Virtual Network.')
+output virtualNetworkResourceId string = virtualNetwork.id
+
+@description('The resource ID of the created Public IP.')
+output publicIPResourceId string = publicIP.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/azure-firewall/tests/e2e/waf-aligned/main.test.bicep b/modules/network/azure-firewall/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..beb7ff6624
--- /dev/null
+++ b/modules/network/azure-firewall/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,190 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.azurefirewalls-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nafwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ publicIPName: 'dep-${namePrefix}-pip-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ vNetId: nestedDependencies.outputs.virtualNetworkResourceId
+ applicationRuleCollections: [
+ {
+ name: 'allow-app-rules'
+ properties: {
+ action: {
+ type: 'allow'
+ }
+ priority: 100
+ rules: [
+ {
+ fqdnTags: [
+ 'AppServiceEnvironment'
+ 'WindowsUpdate'
+ ]
+ name: 'allow-ase-tags'
+ protocols: [
+ {
+ port: '80'
+ protocolType: 'HTTP'
+ }
+ {
+ port: '443'
+ protocolType: 'HTTPS'
+ }
+ ]
+ sourceAddresses: [
+ '*'
+ ]
+ }
+ {
+ name: 'allow-ase-management'
+ protocols: [
+ {
+ port: '80'
+ protocolType: 'HTTP'
+ }
+ {
+ port: '443'
+ protocolType: 'HTTPS'
+ }
+ ]
+ sourceAddresses: [
+ '*'
+ ]
+ targetFqdns: [
+ 'bing.com'
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ publicIPResourceID: nestedDependencies.outputs.publicIPResourceId
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ networkRuleCollections: [
+ {
+ name: 'allow-network-rules'
+ properties: {
+ action: {
+ type: 'allow'
+ }
+ priority: 100
+ rules: [
+ {
+ destinationAddresses: [
+ '*'
+ ]
+ destinationPorts: [
+ '12000'
+ '123'
+ ]
+ name: 'allow-ntp'
+ protocols: [
+ 'Any'
+ ]
+ sourceAddresses: [
+ '*'
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ zones: [
+ '1'
+ '2'
+ '3'
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/bastion-host/README.md b/modules/network/bastion-host/README.md
index 06e8704806..5524340559 100644
--- a/modules/network/bastion-host/README.md
+++ b/modules/network/bastion-host/README.md
@@ -31,6 +31,7 @@ The following section provides usage examples for the module, which were used to
- [Custompip](#example-1-custompip)
- [Using only defaults](#example-2-using-only-defaults)
- [Using large parameter set](#example-3-using-large-parameter-set)
+- [WAF-aligned](#example-4-waf-aligned)
### Example 1: _Custompip_
@@ -351,6 +352,144 @@ module bastionHost 'br:bicep/modules/network.bastion-host:1.0.0' = {
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/bastion-host/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/bastion-host/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..c25af5e3e7
--- /dev/null
+++ b/modules/network/bastion-host/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,59 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Public IP to create.')
+param publicIPName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'AzureBastionSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ }
+ }
+ ]
+ }
+}
+
+resource publicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = {
+ name: publicIPName
+ location: location
+ sku: {
+ name: 'Standard'
+ tier: 'Regional'
+ }
+ properties: {
+ publicIPAllocationMethod: 'Static'
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Virtual Network.')
+output virtualNetworkResourceId string = virtualNetwork.id
+
+@description('The resource ID of the created Public IP.')
+output publicIPResourceId string = publicIP.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/bastion-host/tests/e2e/waf-aligned/main.test.bicep b/modules/network/bastion-host/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..30d7f82891
--- /dev/null
+++ b/modules/network/bastion-host/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,105 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.bastionhosts-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nbhwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ publicIPName: 'dep-${namePrefix}-pip-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ vNetId: nestedDependencies.outputs.virtualNetworkResourceId
+ bastionSubnetPublicIpResourceId: nestedDependencies.outputs.publicIPResourceId
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ disableCopyPaste: true
+ enableFileCopy: false
+ enableIpConnect: false
+ enableShareableLink: false
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ scaleUnits: 4
+ skuName: 'Standard'
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/ddos-protection-plan/README.md b/modules/network/ddos-protection-plan/README.md
index 1ccac70c5a..0a82054e08 100644
--- a/modules/network/ddos-protection-plan/README.md
+++ b/modules/network/ddos-protection-plan/README.md
@@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -163,6 +164,92 @@ module ddosProtectionPlan 'br:bicep/modules/network.ddos-protection-plan:1.0.0'
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/ddos-protection-plan/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/ddos-protection-plan/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/network/ddos-protection-plan/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/ddos-protection-plan/tests/e2e/waf-aligned/main.test.bicep b/modules/network/ddos-protection-plan/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..8bdf24f0bd
--- /dev/null
+++ b/modules/network/ddos-protection-plan/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,72 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.ddosprotectionplans-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'ndppwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/dns-forwarding-ruleset/README.md b/modules/network/dns-forwarding-ruleset/README.md
index 43d21c8605..7f80e40e75 100644
--- a/modules/network/dns-forwarding-ruleset/README.md
+++ b/modules/network/dns-forwarding-ruleset/README.md
@@ -32,6 +32,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -219,6 +220,136 @@ module dnsForwardingRuleset 'br:bicep/modules/network.dns-forwarding-ruleset:1.0
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/dns-forwarding-ruleset/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/dns-forwarding-ruleset/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..d1fb3445ee
--- /dev/null
+++ b/modules/network/dns-forwarding-ruleset/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,81 @@
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the DNS Resolver to create.')
+param dnsResolverName string
+
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: map(range(0, 2), i => {
+ name: 'subnet-${i}'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 25, i)
+ delegations: [
+ {
+ name: 'dnsdel'
+ properties: {
+ serviceName: 'Microsoft.Network/dnsResolvers'
+ }
+ }
+ ]
+ }
+ })
+ }
+}
+
+resource dnsResolver 'Microsoft.Network/dnsResolvers@2022-07-01' = {
+ name: dnsResolverName
+ location: location
+ properties: {
+ virtualNetwork: {
+ id: virtualNetwork.id
+ }
+
+ }
+}
+
+resource outboundEndpoints 'Microsoft.Network/dnsResolvers/outboundEndpoints@2022-07-01' = {
+ name: 'pdnsout'
+ location: location
+ parent: dnsResolver
+ properties: {
+ subnet: {
+ id: virtualNetwork.properties.subnets[1].id
+ }
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Virtual Network.')
+output virtualNetworkResourceId string = virtualNetwork.id
+
+@description('The resource ID of the created inbound endpoint Virtual Network Subnet.')
+output subnetResourceId_dnsIn string = virtualNetwork.properties.subnets[0].id
+
+@description('The resource ID of the created outbound endpoint Virtual Network Subnet.')
+output subnetResourceId_dnsOut string = virtualNetwork.properties.subnets[1].id
+
+@description('The resource ID of the created DNS Resolver.')
+output dnsResolverOutboundEndpointsId string = outboundEndpoints.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/dns-forwarding-ruleset/tests/e2e/waf-aligned/main.test.bicep b/modules/network/dns-forwarding-ruleset/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..d6dfab9955
--- /dev/null
+++ b/modules/network/dns-forwarding-ruleset/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,94 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.dnsForwardingRuleset-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'ndfrswaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ dnsResolverName: 'dep-${namePrefix}-ndr-${serviceShort}'
+ location: location
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ dnsResolverOutboundEndpointResourceIds: [
+ nestedDependencies.outputs.dnsResolverOutboundEndpointsId
+ ]
+ vNetLinks: [
+ nestedDependencies.outputs.virtualNetworkResourceId
+ ]
+ forwardingRules: [
+ {
+ name: 'rule1'
+ forwardingRuleState: 'Enabled'
+ domainName: 'contoso.'
+ targetDnsServers: [
+ {
+ ipAddress: '192.168.0.1'
+ port: '53'
+ }
+ ]
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/dns-resolver/README.md b/modules/network/dns-resolver/README.md
index 99f030c8b2..9dd23b73e9 100644
--- a/modules/network/dns-resolver/README.md
+++ b/modules/network/dns-resolver/README.md
@@ -31,6 +31,7 @@ The following section provides usage examples for the module, which were used to
>**Note**: To reference the module, please use the following syntax `br:bicep/modules/network.dns-resolver:1.0.0`.
- [Using large parameter set](#example-1-using-large-parameter-set)
+- [WAF-aligned](#example-2-waf-aligned)
### Example 1: _Using large parameter set_
@@ -124,6 +125,98 @@ module dnsResolver 'br:bicep/modules/network.dns-resolver:1.0.0' = {
+### Example 2: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/dns-resolver/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/dns-resolver/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..7a174f0fc2
--- /dev/null
+++ b/modules/network/dns-resolver/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,42 @@
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: map(range(0, 2), i => {
+ name: 'subnet-${i}'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 25, i)
+ delegations: [
+ {
+ name: 'dnsdel'
+ properties: {
+ serviceName: 'Microsoft.Network/dnsResolvers'
+ }
+ }
+ ]
+ }
+ })
+ }
+}
+
+@description('The resource ID of the created Virtual Network.')
+output virtualNetworkId string = virtualNetwork.id
+
+@description('The resource ID of the created inbound endpoint Virtual Network Subnet.')
+output subnetResourceId_dnsIn string = virtualNetwork.properties.subnets[0].id
+
+@description('The resource ID of the created outbound endpoint Virtual Network Subnet.')
+output subnetResourceId_dnsOut string = virtualNetwork.properties.subnets[1].id
diff --git a/modules/network/dns-resolver/tests/e2e/waf-aligned/main.test.bicep b/modules/network/dns-resolver/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..8748710b28
--- /dev/null
+++ b/modules/network/dns-resolver/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,75 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.dnsResolvers-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'ndrwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ virtualNetworkId: nestedDependencies.outputs.virtualNetworkId
+ inboundEndpoints: [
+ {
+ name: '${namePrefix}-az-pdnsin-x-001'
+ subnetId: nestedDependencies.outputs.subnetResourceId_dnsIn
+ }
+ ]
+ outboundEndpoints: [
+ {
+ name: '${namePrefix}-az-pdnsout-x-001'
+ subnetId: nestedDependencies.outputs.subnetResourceId_dnsOut
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/dns-zone/README.md b/modules/network/dns-zone/README.md
index 23651a2aa3..003e5548ed 100644
--- a/modules/network/dns-zone/README.md
+++ b/modules/network/dns-zone/README.md
@@ -40,6 +40,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -489,6 +490,406 @@ module dnsZone 'br:bicep/modules/network.dns-zone:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/dns-zone/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/dns-zone/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..22bd417624
--- /dev/null
+++ b/modules/network/dns-zone/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,37 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Traffic Manager Profile to create.')
+param trafficManagerProfileName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource trafficManagerProfile 'Microsoft.Network/trafficmanagerprofiles@2022-04-01-preview' = {
+ name: trafficManagerProfileName
+ location: 'global'
+ properties: {
+ trafficRoutingMethod: 'Performance'
+ maxReturn: 0
+ dnsConfig: {
+ relativeName: trafficManagerProfileName
+ ttl: 60
+ }
+ monitorConfig: {
+ protocol: 'HTTP'
+ port: 80
+ path: '/'
+ }
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Traffic Manager Profile.')
+output trafficManagerProfileResourceId string = trafficManagerProfile.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/dns-zone/tests/e2e/waf-aligned/main.test.bicep b/modules/network/dns-zone/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..6e754253e1
--- /dev/null
+++ b/modules/network/dns-zone/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,222 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.dnszones-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'ndzwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ trafficManagerProfileName: 'dep-${namePrefix}-tmp-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001.com'
+ a: [
+ {
+ aRecords: [
+ {
+ ipv4Address: '10.240.4.4'
+ }
+ ]
+ name: 'A_10.240.4.4'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ ttl: 3600
+ }
+ ]
+ aaaa: [
+ {
+ aaaaRecords: [
+ {
+ ipv6Address: '2001:0db8:85a3:0000:0000:8a2e:0370:7334'
+ }
+ ]
+ name: 'AAAA_2001_0db8_85a3_0000_0000_8a2e_0370_7334'
+ ttl: 3600
+ }
+ ]
+ cname: [
+ {
+ cnameRecord: {
+ cname: 'test'
+ }
+ name: 'CNAME_test'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ ttl: 3600
+ }
+ {
+ name: 'CNAME_aliasRecordSet'
+ targetResourceId: nestedDependencies.outputs.trafficManagerProfileResourceId
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ mx: [
+ {
+ mxRecords: [
+ {
+ exchange: 'contoso.com'
+ preference: 100
+ }
+ ]
+ name: 'MX_contoso'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ ttl: 3600
+ }
+ ]
+ ptr: [
+ {
+ name: 'PTR_contoso'
+ ptrRecords: [
+ {
+ ptrdname: 'contoso.com'
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ ttl: 3600
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ soa: [
+ {
+ name: '@'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ soaRecord: {
+ email: 'azuredns-hostmaster.microsoft.com'
+ expireTime: 2419200
+ host: 'ns1-04.azure-dns.com.'
+ minimumTtl: 300
+ refreshTime: 3600
+ retryTime: 300
+ serialNumber: '1'
+ }
+ ttl: 3600
+ }
+ ]
+ srv: [
+ {
+ name: 'SRV_contoso'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ srvRecords: [
+ {
+ port: 9332
+ priority: 0
+ target: 'test.contoso.com'
+ weight: 0
+ }
+ ]
+ ttl: 3600
+ }
+ ]
+ txt: [
+ {
+ name: 'TXT_test'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ ttl: 3600
+ txtRecords: [
+ {
+ value: [
+ 'test'
+ ]
+ }
+ ]
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/express-route-circuit/README.md b/modules/network/express-route-circuit/README.md
index 125ba3bbb9..1a35356326 100644
--- a/modules/network/express-route-circuit/README.md
+++ b/modules/network/express-route-circuit/README.md
@@ -29,6 +29,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -230,6 +231,146 @@ module expressRouteCircuit 'br:bicep/modules/network.express-route-circuit:1.0.0
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/express-route-circuit/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/express-route-circuit/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/network/express-route-circuit/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/express-route-circuit/tests/e2e/waf-aligned/main.test.bicep b/modules/network/express-route-circuit/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..a7c2a372a3
--- /dev/null
+++ b/modules/network/express-route-circuit/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,106 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.expressroutecircuits-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nercwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ bandwidthInMbps: 50
+ peeringLocation: 'Amsterdam'
+ serviceProviderName: 'Equinix'
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ skuFamily: 'MeteredData'
+ skuTier: 'Standard'
+ allowClassicOperations: true
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/express-route-gateway/README.md b/modules/network/express-route-gateway/README.md
index f396c96058..1804fe9a3f 100644
--- a/modules/network/express-route-gateway/README.md
+++ b/modules/network/express-route-gateway/README.md
@@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -177,6 +178,102 @@ module expressRouteGateway 'br:bicep/modules/network.express-route-gateway:1.0.0
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/express-route-gateway/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/express-route-gateway/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..acaa3b4df8
--- /dev/null
+++ b/modules/network/express-route-gateway/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,38 @@
+@description('Required. The name of the virtual WAN to create.')
+param virtualWANName string
+
+@description('Required. The name of the virtual Hub to create.')
+param virtualHubName string
+
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource virtualWan 'Microsoft.Network/virtualWans@2023-04-01' = {
+ name: virtualWANName
+ location: location
+}
+
+resource virtualHub 'Microsoft.Network/virtualHubs@2023-04-01' = {
+ name: virtualHubName
+ location: location
+ properties: {
+ addressPrefix: '10.0.0.0/16'
+ virtualWan: {
+ id: virtualWan.id
+ }
+ }
+}
+
+@description('The resource ID of the created Virtual Hub.')
+output virtualHubResourceId string = virtualHub.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/express-route-gateway/tests/e2e/waf-aligned/main.test.bicep b/modules/network/express-route-gateway/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..3c237372da
--- /dev/null
+++ b/modules/network/express-route-gateway/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,75 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.expressRouteGateway-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nergwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualWANName: 'dep-${namePrefix}-vwan-${serviceShort}'
+ virtualHubName: 'dep-${namePrefix}-hub-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ hello: 'world'
+ }
+ autoScaleConfigurationBoundsMin: 2
+ autoScaleConfigurationBoundsMax: 3
+ virtualHubId: nestedDependencies.outputs.virtualHubResourceId
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ }
+}
diff --git a/modules/network/firewall-policy/README.md b/modules/network/firewall-policy/README.md
index 1cf5307503..c2a13a1d20 100644
--- a/modules/network/firewall-policy/README.md
+++ b/modules/network/firewall-policy/README.md
@@ -27,6 +27,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -222,6 +223,152 @@ module firewallPolicy 'br:bicep/modules/network.firewall-policy:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/firewall-policy/tests/e2e/waf-aligned/main.test.bicep b/modules/network/firewall-policy/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..2c496ca64e
--- /dev/null
+++ b/modules/network/firewall-policy/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,93 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.firewallpolicies-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nfpwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ ruleCollectionGroups: [
+ {
+ name: '${namePrefix}-rule-001'
+ priority: 5000
+ ruleCollections: [
+ {
+ action: {
+ type: 'Allow'
+ }
+ name: 'collection002'
+ priority: 5555
+ ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
+ rules: [
+ {
+ destinationAddresses: [
+ '*'
+ ]
+ destinationFqdns: []
+ destinationIpGroups: []
+ destinationPorts: [
+ '80'
+ ]
+ ipProtocols: [
+ 'TCP'
+ 'UDP'
+ ]
+ name: 'rule002'
+ ruleType: 'NetworkRule'
+ sourceAddresses: [
+ '*'
+ ]
+ sourceIpGroups: []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ allowSqlRedirect: true
+ autoLearnPrivateRanges: 'Enabled'
+ }
+}
diff --git a/modules/network/front-door-web-application-firewall-policy/README.md b/modules/network/front-door-web-application-firewall-policy/README.md
index c12d09f3bf..45170239e9 100644
--- a/modules/network/front-door-web-application-firewall-policy/README.md
+++ b/modules/network/front-door-web-application-firewall-policy/README.md
@@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -297,6 +298,226 @@ module frontDoorWebApplicationFirewallPolicy 'br:bicep/modules/network.front-doo
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/front-door-web-application-firewall-policy/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/front-door-web-application-firewall-policy/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..7b3d4e8fb0
--- /dev/null
+++ b/modules/network/front-door-web-application-firewall-policy/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/front-door-web-application-firewall-policy/tests/e2e/waf-aligned/main.test.bicep b/modules/network/front-door-web-application-firewall-policy/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..f7f4e7fad3
--- /dev/null
+++ b/modules/network/front-door-web-application-firewall-policy/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,135 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.frontdoorWebApplicationFirewallPolicies-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nagwafpwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ sku: 'Premium_AzureFrontDoor'
+ policySettings: {
+ mode: 'Prevention'
+ redirectUrl: 'http://www.bing.com'
+ customBlockResponseStatusCode: 200
+ customBlockResponseBody: 'PGh0bWw+CjxoZWFkZXI+PHRpdGxlPkhlbGxvPC90aXRsZT48L2hlYWRlcj4KPGJvZHk+CkhlbGxvIHdvcmxkCjwvYm9keT4KPC9odG1sPg=='
+ }
+ customRules: {
+ rules: [
+ {
+ name: 'CustomRule1'
+ priority: 2
+ enabledState: 'Enabled'
+ action: 'Block'
+ ruleType: 'MatchRule'
+ rateLimitDurationInMinutes: 1
+ rateLimitThreshold: 10
+ matchConditions: [
+ {
+ matchVariable: 'RemoteAddr'
+ selector: null
+ operator: 'GeoMatch'
+ negateCondition: false
+ transforms: []
+ matchValue: [
+ 'CH'
+ ]
+ }
+ {
+ matchVariable: 'RequestHeader'
+ selector: 'UserAgent'
+ operator: 'Contains'
+ negateCondition: false
+ transforms: []
+ matchValue: [
+ 'windows'
+ ]
+ }
+ {
+ matchVariable: 'QueryString'
+ operator: 'Contains'
+ negateCondition: false
+ transforms: [
+ 'UrlDecode'
+ 'Lowercase'
+ ]
+ matchValue: [
+ ''
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ managedRules: {
+ managedRuleSets: [
+ {
+ ruleSetType: 'Microsoft_BotManagerRuleSet'
+ ruleSetVersion: '1.0'
+ }
+ ]
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ }
+}
diff --git a/modules/network/front-door/README.md b/modules/network/front-door/README.md
index 02f47b80bd..75bd27f5d6 100644
--- a/modules/network/front-door/README.md
+++ b/modules/network/front-door/README.md
@@ -29,6 +29,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -524,6 +525,284 @@ module frontDoor 'br:bicep/modules/network.front-door:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/front-door/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/front-door/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/network/front-door/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/front-door/tests/e2e/waf-aligned/main.test.bicep b/modules/network/front-door/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..7767577465
--- /dev/null
+++ b/modules/network/front-door/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,161 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.frontdoors-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nfdwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+var resourceName = '${namePrefix}${serviceShort}001'
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: resourceName
+ backendPools: [
+ {
+ name: 'backendPool'
+ properties: {
+ backends: [
+ {
+ address: 'biceptest.local'
+ backendHostHeader: 'backendAddress'
+ enabledState: 'Enabled'
+ httpPort: 80
+ httpsPort: 443
+ priority: 1
+ privateLinkAlias: ''
+ privateLinkApprovalMessage: ''
+ privateLinkLocation: ''
+ privateLinkResourceId: ''
+ weight: 50
+ }
+ ]
+ HealthProbeSettings: {
+ id: '${resourceGroup.id}/providers/Microsoft.Network/frontDoors/${resourceName}/HealthProbeSettings/heathProbe'
+ }
+ LoadBalancingSettings: {
+ id: '${resourceGroup.id}/providers/Microsoft.Network/frontDoors/${resourceName}/LoadBalancingSettings/loadBalancer'
+ }
+ }
+ }
+ ]
+ enforceCertificateNameCheck: 'Disabled'
+ frontendEndpoints: [
+ {
+ name: 'frontEnd'
+ properties: {
+ hostName: '${resourceName}.${environment().suffixes.azureFrontDoorEndpointSuffix}'
+ sessionAffinityEnabledState: 'Disabled'
+ sessionAffinityTtlSeconds: 60
+ }
+ }
+ ]
+ healthProbeSettings: [
+ {
+ name: 'heathProbe'
+ properties: {
+ enabledState: ''
+ healthProbeMethod: ''
+ intervalInSeconds: 60
+ path: '/'
+ protocol: 'Https'
+ }
+ }
+ ]
+ loadBalancingSettings: [
+ {
+ name: 'loadBalancer'
+ properties: {
+ additionalLatencyMilliseconds: 0
+ sampleSize: 50
+ successfulSamplesRequired: 1
+ }
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ routingRules: [
+ {
+ name: 'routingRule'
+ properties: {
+ acceptedProtocols: [
+ 'Http'
+ 'Https'
+ ]
+ enabledState: 'Enabled'
+ frontendEndpoints: [
+ {
+ id: '${resourceGroup.id}/providers/Microsoft.Network/frontDoors/${resourceName}/FrontendEndpoints/frontEnd'
+ }
+ ]
+ patternsToMatch: [
+ '/*'
+ ]
+ routeConfiguration: {
+ '@odata.type': '#Microsoft.Azure.FrontDoor.Models.FrontdoorForwardingConfiguration'
+ backendPool: {
+ id: '${resourceGroup.id}/providers/Microsoft.Network/frontDoors/${resourceName}/BackendPools/backendPool'
+ }
+ forwardingProtocol: 'MatchRequest'
+ }
+ }
+ }
+ ]
+ sendRecvTimeoutSeconds: 10
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/ip-group/README.md b/modules/network/ip-group/README.md
index 36b3fe51fa..d9706dfeb2 100644
--- a/modules/network/ip-group/README.md
+++ b/modules/network/ip-group/README.md
@@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -173,6 +174,102 @@ module ipGroup 'br:bicep/modules/network.ip-group:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/ip-group/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/ip-group/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/network/ip-group/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/ip-group/tests/e2e/waf-aligned/main.test.bicep b/modules/network/ip-group/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..124d1cdf86
--- /dev/null
+++ b/modules/network/ip-group/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,76 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.ipgroups-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nigwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ ipAddresses: [
+ '10.0.0.1'
+ '10.0.0.2'
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/load-balancer/README.md b/modules/network/load-balancer/README.md
index b747882d68..cb030c747e 100644
--- a/modules/network/load-balancer/README.md
+++ b/modules/network/load-balancer/README.md
@@ -33,6 +33,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Internal](#example-2-internal)
- [Using large parameter set](#example-3-using-large-parameter-set)
+- [WAF-aligned](#example-4-waf-aligned)
### Example 1: _Using only defaults_
@@ -609,6 +610,294 @@ module loadBalancer 'br:bicep/modules/network.load-balancer:1.0.0' = {
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/load-balancer/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/load-balancer/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..c54f364b82
--- /dev/null
+++ b/modules/network/load-balancer/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,36 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Public IP to create.')
+param publicIPName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource publicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = {
+ name: publicIPName
+ location: location
+ sku: {
+ name: 'Standard'
+ tier: 'Regional'
+ }
+ properties: {
+ publicIPAllocationMethod: 'Static'
+ }
+ zones: [
+ '1'
+ '2'
+ '3'
+ ]
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Public IP.')
+output publicIPResourceId string = publicIP.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/load-balancer/tests/e2e/waf-aligned/main.test.bicep b/modules/network/load-balancer/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..f0a9319226
--- /dev/null
+++ b/modules/network/load-balancer/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,181 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.loadbalancers-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nlbwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ publicIPName: 'dep-${namePrefix}-pip-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ frontendIPConfigurations: [
+ {
+ name: 'publicIPConfig1'
+ publicIPAddressId: nestedDependencies.outputs.publicIPResourceId
+ }
+ ]
+ backendAddressPools: [
+ {
+ name: 'backendAddressPool1'
+ }
+ {
+ name: 'backendAddressPool2'
+ }
+ ]
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ inboundNatRules: [
+ {
+ backendPort: 443
+ enableFloatingIP: false
+ enableTcpReset: false
+ frontendIPConfigurationName: 'publicIPConfig1'
+ frontendPort: 443
+ idleTimeoutInMinutes: 4
+ name: 'inboundNatRule1'
+ protocol: 'Tcp'
+ }
+ {
+ backendPort: 3389
+ frontendIPConfigurationName: 'publicIPConfig1'
+ frontendPort: 3389
+ name: 'inboundNatRule2'
+ }
+ ]
+ loadBalancingRules: [
+ {
+ backendAddressPoolName: 'backendAddressPool1'
+ backendPort: 80
+ disableOutboundSnat: true
+ enableFloatingIP: false
+ enableTcpReset: false
+ frontendIPConfigurationName: 'publicIPConfig1'
+ frontendPort: 80
+ idleTimeoutInMinutes: 5
+ loadDistribution: 'Default'
+ name: 'publicIPLBRule1'
+ probeName: 'probe1'
+ protocol: 'Tcp'
+ }
+ {
+ backendAddressPoolName: 'backendAddressPool2'
+ backendPort: 8080
+ frontendIPConfigurationName: 'publicIPConfig1'
+ frontendPort: 8080
+ loadDistribution: 'Default'
+ name: 'publicIPLBRule2'
+ probeName: 'probe2'
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ outboundRules: [
+ {
+ allocatedOutboundPorts: 63984
+ backendAddressPoolName: 'backendAddressPool1'
+ frontendIPConfigurationName: 'publicIPConfig1'
+ name: 'outboundRule1'
+ }
+ ]
+ probes: [
+ {
+ intervalInSeconds: 10
+ name: 'probe1'
+ numberOfProbes: 5
+ port: 80
+ protocol: 'Tcp'
+ }
+ {
+ name: 'probe2'
+ port: 443
+ protocol: 'Https'
+ requestPath: '/'
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/local-network-gateway/README.md b/modules/network/local-network-gateway/README.md
index cc2167d281..2b5cac74a2 100644
--- a/modules/network/local-network-gateway/README.md
+++ b/modules/network/local-network-gateway/README.md
@@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -195,6 +196,112 @@ module localNetworkGateway 'br:bicep/modules/network.local-network-gateway:1.0.0
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/local-network-gateway/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/local-network-gateway/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/network/local-network-gateway/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/local-network-gateway/tests/e2e/waf-aligned/main.test.bicep b/modules/network/local-network-gateway/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..e47e0f4ebc
--- /dev/null
+++ b/modules/network/local-network-gateway/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,78 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.localnetworkgateways-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nlngwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ localAddressPrefixes: [
+ '192.168.1.0/24'
+ ]
+ localGatewayPublicIpAddress: '8.8.8.8'
+ localAsn: '65123'
+ localBgpPeeringAddress: '192.168.1.5'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/nat-gateway/README.md b/modules/network/nat-gateway/README.md
index d848af2b74..b764e57c4d 100644
--- a/modules/network/nat-gateway/README.md
+++ b/modules/network/nat-gateway/README.md
@@ -31,6 +31,7 @@ The following section provides usage examples for the module, which were used to
- [Using large parameter set](#example-1-using-large-parameter-set)
- [Combine a generated and provided Public IP Prefix](#example-2-combine-a-generated-and-provided-public-ip-prefix)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using large parameter set_
@@ -312,6 +313,158 @@ module natGateway 'br:bicep/modules/network.nat-gateway:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/nat-gateway/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/nat-gateway/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/network/nat-gateway/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/nat-gateway/tests/e2e/waf-aligned/main.test.bicep b/modules/network/nat-gateway/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..024f35b432
--- /dev/null
+++ b/modules/network/nat-gateway/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,118 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.natgateways-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nngwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ publicIPAddressObjects: [
+ {
+ name: '${namePrefix}${serviceShort}001-pip'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ skuTier: 'Regional'
+ zones: [
+ '1'
+ '2'
+ '3'
+ ]
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/network-interface/README.md b/modules/network/network-interface/README.md
index 95f9eb34e1..0efe82db56 100644
--- a/modules/network/network-interface/README.md
+++ b/modules/network/network-interface/README.md
@@ -31,6 +31,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -260,6 +261,172 @@ module networkInterface 'br:bicep/modules/network.network-interface:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/network-interface/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/network-interface/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..b3a10d32f6
--- /dev/null
+++ b/modules/network/network-interface/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,113 @@
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the Application Security Group to create.')
+param applicationSecurityGroupName string
+
+@description('Required. The name of the Load Balancer Backend Address Pool to create.')
+param loadBalancerName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ }
+ }
+ ]
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource applicationSecurityGroup 'Microsoft.Network/applicationSecurityGroups@2023-04-01' = {
+ name: applicationSecurityGroupName
+ location: location
+}
+
+resource loadBalancer 'Microsoft.Network/loadBalancers@2023-04-01' = {
+ name: loadBalancerName
+ location: location
+ sku: {
+ name: 'Standard'
+ }
+
+ properties: {
+ frontendIPConfigurations: [
+ {
+ name: 'privateIPConfig1'
+ properties: {
+ subnet: {
+ id: virtualNetwork.properties.subnets[0].id
+ }
+ }
+ }
+ ]
+ }
+
+ resource backendPool 'backendAddressPools@2022-01-01' = {
+ name: 'default'
+ }
+}
+
+resource inboundNatRule 'Microsoft.Network/loadBalancers/inboundNatRules@2023-04-01' = {
+ name: 'inboundNatRule1'
+ properties: {
+ frontendPort: 443
+ backendPort: 443
+ enableFloatingIP: false
+ enableTcpReset: false
+ frontendIPConfiguration: {
+ id: loadBalancer.properties.frontendIPConfigurations[0].id
+ }
+ idleTimeoutInMinutes: 4
+ protocol: 'Tcp'
+ }
+ parent: loadBalancer
+}
+
+resource inboundNatRule2 'Microsoft.Network/loadBalancers/inboundNatRules@2023-04-01' = {
+ name: 'inboundNatRule2'
+ properties: {
+ frontendPort: 3389
+ backendPort: 3389
+ frontendIPConfiguration: {
+ id: loadBalancer.properties.frontendIPConfigurations[0].id
+ }
+ idleTimeoutInMinutes: 4
+ protocol: 'Tcp'
+ }
+ parent: loadBalancer
+}
+
+@description('The resource ID of the created Virtual Network Subnet.')
+output subnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Application Security Group.')
+output applicationSecurityGroupResourceId string = applicationSecurityGroup.id
+
+@description('The resource ID of the created Load Balancer Backend Pool Name.')
+output loadBalancerBackendPoolResourceId string = loadBalancer::backendPool.id
diff --git a/modules/network/network-interface/tests/e2e/waf-aligned/main.test.bicep b/modules/network/network-interface/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..218c13495c
--- /dev/null
+++ b/modules/network/network-interface/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,127 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.networkinterfaces-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nniwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ applicationSecurityGroupName: 'dep-${namePrefix}-asg-${serviceShort}'
+ loadBalancerName: 'dep-${namePrefix}-lb-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ ipConfigurations: [
+ {
+ applicationSecurityGroups: [
+ {
+ id: nestedDependencies.outputs.applicationSecurityGroupResourceId
+ }
+ ]
+ loadBalancerBackendAddressPools: [
+ {
+ id: nestedDependencies.outputs.loadBalancerBackendPoolResourceId
+ }
+ ]
+ name: 'ipconfig01'
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ }
+ {
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ applicationSecurityGroups: [
+ {
+ id: nestedDependencies.outputs.applicationSecurityGroupResourceId
+ }
+ ]
+ }
+ ]
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/network-manager/README.md b/modules/network/network-manager/README.md
index 4870ad088b..896d0bd79c 100644
--- a/modules/network/network-manager/README.md
+++ b/modules/network/network-manager/README.md
@@ -35,6 +35,7 @@ The following section provides usage examples for the module, which were used to
>**Note**: To reference the module, please use the following syntax `br:bicep/modules/network.network-manager:1.0.0`.
- [Using large parameter set](#example-1-using-large-parameter-set)
+- [WAF-aligned](#example-2-waf-aligned)
### Example 1: _Using large parameter set_
@@ -486,6 +487,456 @@ module networkManager 'br:bicep/modules/network.network-manager:1.0.0' = {
+### Example 2: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/network-manager/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/network-manager/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..501a5a13c0
--- /dev/null
+++ b/modules/network/network-manager/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,96 @@
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the Hub Virtual Network to create.')
+param virtualNetworkHubName string
+
+@description('Required. The name of the Spoke 1 Virtual Network to create.')
+param virtualNetworkSpoke1Name string
+
+@description('Required. The name of the Spoke 2 Virtual Network to create.')
+param virtualNetworkSpoke2Name string
+
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
+ name: managedIdentityName
+ location: location
+}
+
+var addressPrefixHub = '10.0.0.0/16'
+var addressPrefixSpoke1 = '172.16.0.0/12'
+var addressPrefixSpoke2 = '192.168.0.0/16'
+var subnetName = 'defaultSubnet'
+
+resource virtualNetworkHub 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkHubName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefixHub
+ ]
+ }
+ subnets: [
+ {
+ name: subnetName
+ properties: {
+ addressPrefix: addressPrefixHub
+ }
+ }
+ ]
+ }
+}
+
+resource virtualNetworkSpoke1 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkSpoke1Name
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefixSpoke1
+ ]
+ }
+ subnets: [
+ {
+ name: subnetName
+ properties: {
+ addressPrefix: addressPrefixSpoke1
+ }
+ }
+ ]
+ }
+}
+
+resource virtualNetworkSpoke2 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkSpoke2Name
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefixSpoke2
+ ]
+ }
+ subnets: [
+ {
+ name: subnetName
+ properties: {
+ addressPrefix: addressPrefixSpoke2
+ }
+ }
+ ]
+ }
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Hub Virtual Network.')
+output virtualNetworkHubId string = virtualNetworkHub.id
+
+@description('The resource ID of the created Spoke 1 Virtual Network.')
+output virtualNetworkSpoke1Id string = virtualNetworkSpoke1.id
+
+@description('The resource ID of the created Spoke 2 Virtual Network.')
+output virtualNetworkSpoke2Id string = virtualNetworkSpoke2.id
diff --git a/modules/network/network-manager/tests/e2e/waf-aligned/main.test.bicep b/modules/network/network-manager/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..0b70f2b7b8
--- /dev/null
+++ b/modules/network/network-manager/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,255 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.networkmanagers-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nnmwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ virtualNetworkHubName: 'dep-${namePrefix}-vnetHub-${serviceShort}'
+ virtualNetworkSpoke1Name: 'dep-${namePrefix}-vnetSpoke1-${serviceShort}'
+ virtualNetworkSpoke2Name: 'dep-${namePrefix}-vnetSpoke2-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+var networkManagerName = '${namePrefix}${serviceShort}001'
+var networkManagerExpecetedResourceID = '${resourceGroup.id}/providers/Microsoft.Network/networkManagers/${networkManagerName}'
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ name: networkManagerName
+ enableDefaultTelemetry: enableDefaultTelemetry
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ networkManagerScopeAccesses: [
+ 'Connectivity'
+ 'SecurityAdmin'
+ ]
+ networkManagerScopes: {
+ subscriptions: [
+ subscription().id
+ ]
+ }
+ networkGroups: [
+ {
+ name: 'network-group-spokes'
+ description: 'network-group-spokes description'
+ staticMembers: [
+ {
+ name: 'virtualNetworkSpoke1'
+ resourceId: nestedDependencies.outputs.virtualNetworkSpoke1Id
+ }
+ {
+ name: 'virtualNetworkSpoke2'
+ resourceId: nestedDependencies.outputs.virtualNetworkSpoke2Id
+ }
+ ]
+ }
+ ]
+ connectivityConfigurations: [
+ {
+ name: 'hubSpokeConnectivity'
+ description: 'hubSpokeConnectivity description'
+ connectivityTopology: 'HubAndSpoke'
+ hubs: [
+ {
+ resourceId: nestedDependencies.outputs.virtualNetworkHubId
+ resourceType: 'Microsoft.Network/virtualNetworks'
+ }
+ ]
+ deleteExistingPeering: 'True'
+ isGlobal: 'True'
+ appliesToGroups: [
+ {
+ networkGroupId: '${networkManagerExpecetedResourceID}/networkGroups/network-group-spokes'
+ useHubGateway: 'False'
+ groupConnectivity: 'None'
+ isGlobal: 'False'
+ }
+ ]
+ }
+ {
+ name: 'MeshConnectivity'
+ description: 'MeshConnectivity description'
+ connectivityTopology: 'Mesh'
+ deleteExistingPeering: 'True'
+ isGlobal: 'True'
+ appliesToGroups: [
+ {
+ networkGroupId: '${networkManagerExpecetedResourceID}/networkGroups/network-group-spokes'
+ useHubGateway: 'False'
+ groupConnectivity: 'None'
+ isGlobal: 'False'
+ }
+ ]
+ }
+ ]
+ scopeConnections: [
+ {
+ name: 'scope-connection-test'
+ description: 'description of the scope connection'
+ resourceId: subscription().id
+ tenantid: tenant().tenantId
+ }
+ ]
+ securityAdminConfigurations: [
+ {
+ name: 'test-security-admin-config'
+ description: 'description of the security admin config'
+ applyOnNetworkIntentPolicyBasedServices: [
+ 'AllowRulesOnly'
+ ]
+ ruleCollections: [
+ {
+ name: 'test-rule-collection-1'
+ description: 'test-rule-collection-description'
+ appliesToGroups: [
+ {
+ networkGroupId: '${networkManagerExpecetedResourceID}/networkGroups/network-group-spokes'
+ }
+ ]
+ rules: [
+ {
+ name: 'test-inbound-allow-rule-1'
+ description: 'test-inbound-allow-rule-1-description'
+ access: 'Allow'
+ direction: 'Inbound'
+ priority: 150
+ protocol: 'Tcp'
+ }
+ {
+ name: 'test-outbound-deny-rule-2'
+ description: 'test-outbound-deny-rule-2-description'
+ access: 'Deny'
+ direction: 'Outbound'
+ priority: 200
+ protocol: 'Tcp'
+ sourcePortRanges: [
+ '80'
+ '442-445'
+ ]
+ sources: [
+ {
+ addressPrefix: 'AppService.WestEurope'
+ addressPrefixType: 'ServiceTag'
+ }
+ ]
+ }
+ ]
+ }
+ {
+ name: 'test-rule-collection-2'
+ description: 'test-rule-collection-description'
+ appliesToGroups: [
+ {
+ networkGroupId: '${networkManagerExpecetedResourceID}/networkGroups/network-group-spokes'
+ }
+ ]
+ rules: [
+ {
+ name: 'test-inbound-allow-rule-3'
+ description: 'test-inbound-allow-rule-3-description'
+ access: 'Allow'
+ direction: 'Inbound'
+ destinationPortRanges: [
+ '80'
+ '442-445'
+ ]
+ destinations: [
+ {
+ addressPrefix: '192.168.20.20'
+ addressPrefixType: 'IPPrefix'
+ }
+ ]
+ priority: 250
+ protocol: 'Tcp'
+ }
+ {
+ name: 'test-inbound-allow-rule-4'
+ description: 'test-inbound-allow-rule-4-description'
+ access: 'Allow'
+ direction: 'Inbound'
+ sources: [
+ {
+ addressPrefix: '10.0.0.0/24'
+ addressPrefixType: 'IPPrefix'
+ }
+ {
+ addressPrefix: '100.100.100.100'
+ addressPrefixType: 'IPPrefix'
+ }
+ ]
+ destinations: [
+ {
+ addressPrefix: '172.16.0.0/24'
+ addressPrefixType: 'IPPrefix'
+ }
+ {
+ addressPrefix: '172.16.1.0/24'
+ addressPrefixType: 'IPPrefix'
+ }
+ ]
+ priority: 260
+ protocol: 'Tcp'
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/network-security-group/README.md b/modules/network/network-security-group/README.md
index f5802ad688..416644df15 100644
--- a/modules/network/network-security-group/README.md
+++ b/modules/network/network-security-group/README.md
@@ -30,6 +30,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -315,6 +316,242 @@ module networkSecurityGroup 'br:bicep/modules/network.network-security-group:1.0
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/network-security-group/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/network-security-group/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..951c71af97
--- /dev/null
+++ b/modules/network/network-security-group/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,24 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the Application Security Group to create.')
+param applicationSecurityGroupName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource applicationSecurityGroup 'Microsoft.Network/applicationSecurityGroups@2023-04-01' = {
+ name: applicationSecurityGroupName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Application Security Group.')
+output applicationSecurityGroupResourceId string = applicationSecurityGroup.id
diff --git a/modules/network/network-security-group/tests/e2e/waf-aligned/main.test.bicep b/modules/network/network-security-group/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..7c9ac93549
--- /dev/null
+++ b/modules/network/network-security-group/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,160 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.networksecuritygroups-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nnsgwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ applicationSecurityGroupName: 'dep-${namePrefix}-asg-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ securityRules: [
+ {
+ name: 'Specific'
+ properties: {
+ access: 'Allow'
+ description: 'Tests specific IPs and ports'
+ destinationAddressPrefix: '*'
+ destinationPortRange: '8080'
+ direction: 'Inbound'
+ priority: 100
+ protocol: '*'
+ sourceAddressPrefix: '*'
+ sourcePortRange: '*'
+ }
+ }
+ {
+ name: 'Ranges'
+ properties: {
+ access: 'Allow'
+ description: 'Tests Ranges'
+ destinationAddressPrefixes: [
+ '10.2.0.0/16'
+ '10.3.0.0/16'
+ ]
+ destinationPortRanges: [
+ '90'
+ '91'
+ ]
+ direction: 'Inbound'
+ priority: 101
+ protocol: '*'
+ sourceAddressPrefixes: [
+ '10.0.0.0/16'
+ '10.1.0.0/16'
+ ]
+ sourcePortRanges: [
+ '80'
+ '81'
+ ]
+ }
+ }
+ {
+ name: 'Port_8082'
+ properties: {
+ access: 'Allow'
+ description: 'Allow inbound access on TCP 8082'
+ destinationApplicationSecurityGroups: [
+ {
+ id: nestedDependencies.outputs.applicationSecurityGroupResourceId
+ }
+ ]
+ destinationPortRange: '8082'
+ direction: 'Inbound'
+ priority: 102
+ protocol: '*'
+ sourceApplicationSecurityGroups: [
+ {
+ id: nestedDependencies.outputs.applicationSecurityGroupResourceId
+ }
+ ]
+ sourcePortRange: '*'
+ }
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/network-watcher/README.md b/modules/network/network-watcher/README.md
index ede8d1e3a8..a9c59a060f 100644
--- a/modules/network/network-watcher/README.md
+++ b/modules/network/network-watcher/README.md
@@ -30,6 +30,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -293,6 +294,224 @@ module networkWatcher 'br:bicep/modules/network.network-watcher:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/network-watcher/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/network-watcher/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..c20f841f30
--- /dev/null
+++ b/modules/network/network-watcher/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,144 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the first Network Security Group to create.')
+param firstNetworkSecurityGroupName string
+
+@description('Required. The name of the second Network Security Group to create.')
+param secondNetworkSecurityGroupName string
+
+@description('Required. The name of the Virtual Machine to create.')
+param virtualMachineName string
+
+@description('Optional. The password to leverage for the VM login.')
+@secure()
+param password string = newGuid()
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ }
+ }
+ ]
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource firstNetworkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = {
+ name: firstNetworkSecurityGroupName
+ location: location
+}
+
+resource secondNetworkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = {
+ name: secondNetworkSecurityGroupName
+ location: location
+}
+
+resource networkInterface 'Microsoft.Network/networkInterfaces@2023-04-01' = {
+ name: '${virtualMachineName}-nic'
+ location: location
+ properties: {
+ ipConfigurations: [
+ {
+ name: 'ipconfig01'
+ properties: {
+ subnet: {
+ id: virtualNetwork.properties.subnets[0].id
+ }
+ }
+ }
+ ]
+ }
+}
+
+resource virtualMachine 'Microsoft.Compute/virtualMachines@2022-08-01' = {
+ name: virtualMachineName
+ location: location
+ properties: {
+ networkProfile: {
+ networkInterfaces: [
+ {
+ id: networkInterface.id
+ properties: {
+ deleteOption: 'Delete'
+ primary: true
+ }
+ }
+ ]
+ }
+ storageProfile: {
+ imageReference: {
+ publisher: 'Canonical'
+ offer: '0001-com-ubuntu-server-jammy'
+ sku: '22_04-lts-gen2'
+ version: 'latest'
+ }
+ osDisk: {
+ deleteOption: 'Delete'
+ createOption: 'FromImage'
+ }
+ }
+ hardwareProfile: {
+ vmSize: 'Standard_B1ms'
+ }
+ osProfile: {
+ adminUsername: '${virtualMachineName}cake'
+ adminPassword: password
+ computerName: virtualMachineName
+ linuxConfiguration: {
+ disablePasswordAuthentication: false
+ }
+ }
+ }
+}
+
+resource extension 'Microsoft.Compute/virtualMachines/extensions@2021-07-01' = {
+ name: 'NetworkWatcherAgent'
+ parent: virtualMachine
+ location: location
+ properties: {
+ publisher: 'Microsoft.Azure.NetworkWatcher'
+ type: 'NetworkWatcherAgentLinux'
+ typeHandlerVersion: '1.4'
+ autoUpgradeMinorVersion: true
+ enableAutomaticUpgrade: false
+ settings: {}
+ protectedSettings: {}
+ suppressFailures: false
+ }
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Virtual Machine.')
+output virtualMachineResourceId string = virtualMachine.id
+
+@description('The resource ID of the first created Network Security Group.')
+output firstNetworkSecurityGroupResourceId string = firstNetworkSecurityGroup.id
+
+@description('The resource ID of the second created Network Security Group.')
+output secondNetworkSecurityGroupResourceId string = secondNetworkSecurityGroup.id
diff --git a/modules/network/network-watcher/tests/e2e/waf-aligned/main.test.bicep b/modules/network/network-watcher/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..730c05be9e
--- /dev/null
+++ b/modules/network/network-watcher/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,158 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'NetworkWatcherRG' // Note, this is the default NetworkWatcher resource group. Do not change.
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nnwwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ firstNetworkSecurityGroupName: 'dep-${namePrefix}-nsg-1-${serviceShort}'
+ secondNetworkSecurityGroupName: 'dep-${namePrefix}-nsg-2-${serviceShort}'
+ virtualMachineName: 'dep-${namePrefix}-vm-${serviceShort}'
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ location: location
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+#disable-next-line no-hardcoded-location // Disabled as the default RG & location are created in always one location, but each test has to deploy into a different one
+var testLocation = 'westeurope'
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, testLocation)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: 'NetworkWatcher_${testLocation}'
+ location: testLocation
+ connectionMonitors: [
+ {
+ name: '${namePrefix}-${serviceShort}-cm-001'
+ endpoints: [
+ {
+ name: '${namePrefix}-subnet-001(${resourceGroup.name})'
+ resourceId: nestedDependencies.outputs.virtualMachineResourceId
+ type: 'AzureVM'
+ }
+ {
+ address: 'www.bing.com'
+ name: 'Bing'
+ type: 'ExternalAddress'
+ }
+ ]
+ testConfigurations: [
+ {
+ httpConfiguration: {
+ method: 'Get'
+ port: 80
+ preferHTTPS: false
+ requestHeaders: []
+ validStatusCodeRanges: [
+ '200'
+ ]
+ }
+ name: 'HTTP Bing Test'
+ protocol: 'Http'
+ successThreshold: {
+ checksFailedPercent: 5
+ roundTripTimeMs: 100
+ }
+ testFrequencySec: 30
+ }
+ ]
+ testGroups: [
+ {
+ destinations: [
+ 'Bing'
+ ]
+ disable: false
+ name: 'test-http-Bing'
+ sources: [
+ '${namePrefix}-subnet-001(${resourceGroup.name})'
+ ]
+ testConfigurations: [
+ 'HTTP Bing Test'
+ ]
+ }
+ ]
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ flowLogs: [
+ {
+ enabled: false
+ storageId: diagnosticDependencies.outputs.storageAccountResourceId
+ targetResourceId: nestedDependencies.outputs.firstNetworkSecurityGroupResourceId
+ }
+ {
+ formatVersion: 1
+ name: '${namePrefix}-${serviceShort}-fl-001'
+ retentionInDays: 8
+ storageId: diagnosticDependencies.outputs.storageAccountResourceId
+ targetResourceId: nestedDependencies.outputs.secondNetworkSecurityGroupResourceId
+ trafficAnalyticsInterval: 10
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/private-dns-zone/README.md b/modules/network/private-dns-zone/README.md
index ceb0935638..714eea7f96 100644
--- a/modules/network/private-dns-zone/README.md
+++ b/modules/network/private-dns-zone/README.md
@@ -39,6 +39,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -494,6 +495,412 @@ module privateDnsZone 'br:bicep/modules/network.private-dns-zone:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/private-dns-zone/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/private-dns-zone/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..f4ff1fbf54
--- /dev/null
+++ b/modules/network/private-dns-zone/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,41 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ }
+ }
+ ]
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Virtual Network.')
+output virtualNetworkResourceId string = virtualNetwork.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/private-dns-zone/tests/e2e/waf-aligned/main.test.bicep b/modules/network/private-dns-zone/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..116e5bb75b
--- /dev/null
+++ b/modules/network/private-dns-zone/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,224 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.privatednszones-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'npdzwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001.com'
+ a: [
+ {
+ aRecords: [
+ {
+ ipv4Address: '10.240.4.4'
+ }
+ ]
+ name: 'A_10.240.4.4'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ ttl: 3600
+ }
+ ]
+ aaaa: [
+ {
+ aaaaRecords: [
+ {
+ ipv6Address: '2001:0db8:85a3:0000:0000:8a2e:0370:7334'
+ }
+ ]
+ name: 'AAAA_2001_0db8_85a3_0000_0000_8a2e_0370_7334'
+ ttl: 3600
+ }
+ ]
+ cname: [
+ {
+ cnameRecord: {
+ cname: 'test'
+ }
+ name: 'CNAME_test'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ ttl: 3600
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ mx: [
+ {
+ mxRecords: [
+ {
+ exchange: 'contoso.com'
+ preference: 100
+ }
+ ]
+ name: 'MX_contoso'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ ttl: 3600
+ }
+ ]
+ ptr: [
+ {
+ name: 'PTR_contoso'
+ ptrRecords: [
+ {
+ ptrdname: 'contoso.com'
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ ttl: 3600
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ soa: [
+ {
+ name: '@'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ soaRecord: {
+ email: 'azureprivatedns-host.microsoft.com'
+ expireTime: 2419200
+ host: 'azureprivatedns.net'
+ minimumTtl: 10
+ refreshTime: 3600
+ retryTime: 300
+ serialNumber: '1'
+ }
+ ttl: 3600
+ }
+ ]
+ srv: [
+ {
+ name: 'SRV_contoso'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ srvRecords: [
+ {
+ port: 9332
+ priority: 0
+ target: 'test.contoso.com'
+ weight: 0
+ }
+ ]
+ ttl: 3600
+ }
+ ]
+ txt: [
+ {
+ name: 'TXT_test'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ ttl: 3600
+ txtRecords: [
+ {
+ value: [
+ 'test'
+ ]
+ }
+ ]
+ }
+ ]
+ virtualNetworkLinks: [
+ {
+ registrationEnabled: true
+ virtualNetworkResourceId: nestedDependencies.outputs.virtualNetworkResourceId
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/private-endpoint/README.md b/modules/network/private-endpoint/README.md
index e23c6bb6b9..866ff9fecc 100644
--- a/modules/network/private-endpoint/README.md
+++ b/modules/network/private-endpoint/README.md
@@ -31,6 +31,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -258,6 +259,168 @@ module privateEndpoint 'br:bicep/modules/network.private-endpoint:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/private-endpoint/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/private-endpoint/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a4bc9dabca
--- /dev/null
+++ b/modules/network/private-endpoint/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,95 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Key Vault to create.')
+param keyVaultName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the Application Security Group to create.')
+param applicationSecurityGroupName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ }
+ }
+ ]
+ }
+}
+
+resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
+ name: keyVaultName
+ location: location
+ properties: {
+ sku: {
+ family: 'A'
+ name: 'standard'
+ }
+ tenantId: tenant().tenantId
+ enablePurgeProtection: null
+ enabledForTemplateDeployment: true
+ enabledForDiskEncryption: true
+ enabledForDeployment: true
+ enableRbacAuthorization: true
+ accessPolicies: []
+ }
+}
+
+resource applicationSecurityGroup 'Microsoft.Network/applicationSecurityGroups@2023-04-01' = {
+ name: applicationSecurityGroupName
+ location: location
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
+ name: 'privatelink.vaultcore.azure.net'
+ location: 'global'
+
+ resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = {
+ name: '${virtualNetwork.name}-vnetlink'
+ location: 'global'
+ properties: {
+ virtualNetwork: {
+ id: virtualNetwork.id
+ }
+ registrationEnabled: false
+ }
+ }
+}
+
+@description('The resource ID of the created Virtual Network Subnet.')
+output subnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The resource ID of the created Key Vault.')
+output keyVaultResourceId string = keyVault.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Private DNS Zone.')
+output privateDNSZoneResourceId string = privateDNSZone.id
+
+@description('The resource ID of the created Application Security Group.')
+output applicationSecurityGroupResourceId string = applicationSecurityGroup.id
diff --git a/modules/network/private-endpoint/tests/e2e/waf-aligned/main.test.bicep b/modules/network/private-endpoint/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..4e7c2b4c1f
--- /dev/null
+++ b/modules/network/private-endpoint/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,105 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.privateendpoints-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'npewaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ applicationSecurityGroupName: 'dep-${namePrefix}-asg-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ groupIds: [
+ 'vault'
+ ]
+ serviceResourceId: nestedDependencies.outputs.keyVaultResourceId
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.privateDNSZoneResourceId
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ ipConfigurations: [
+ {
+ name: 'myIPconfig'
+ properties: {
+ groupId: 'vault'
+ memberName: 'default'
+ privateIPAddress: '10.0.0.10'
+ }
+ }
+ ]
+ customDnsConfigs: [
+ {
+ fqdn: 'abc.keyvault.com'
+ ipAddresses: [
+ '10.0.0.10'
+ ]
+ }
+ ]
+ customNetworkInterfaceName: '${namePrefix}${serviceShort}001nic'
+ applicationSecurityGroupResourceIds: [
+ nestedDependencies.outputs.applicationSecurityGroupResourceId
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/private-link-service/README.md b/modules/network/private-link-service/README.md
index 45f9b300e1..a2ba040a35 100644
--- a/modules/network/private-link-service/README.md
+++ b/modules/network/private-link-service/README.md
@@ -29,6 +29,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -274,6 +275,168 @@ module privateLinkService 'br:bicep/modules/network.private-link-service:1.0.0'
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/private-link-service/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/private-link-service/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..1031dd4830
--- /dev/null
+++ b/modules/network/private-link-service/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,68 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Load Balancer to create.')
+param loadBalancerName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ privateLinkServiceNetworkPolicies: 'Disabled'
+ }
+ }
+ ]
+ }
+}
+
+resource loadBalancer 'Microsoft.Network/loadBalancers@2023-04-01' = {
+ name: loadBalancerName
+ location: location
+ sku: {
+ name: 'Standard'
+ }
+ properties: {
+ frontendIPConfigurations: [
+ {
+ name: 'frontendIPConfiguration'
+ properties: {
+ subnet: {
+ id: virtualNetwork.properties.subnets[0].id
+ }
+ }
+ }
+ ]
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Virtual Network Subnet.')
+output subnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Load Balancer Frontend IP Configuration.')
+output loadBalancerFrontendIpConfigurationResourceId string = loadBalancer.properties.frontendIPConfigurations[0].id
diff --git a/modules/network/private-link-service/tests/e2e/waf-aligned/main.test.bicep b/modules/network/private-link-service/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..c327e89f13
--- /dev/null
+++ b/modules/network/private-link-service/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,106 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.privatelinkservices-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nplswaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ loadBalancerName: 'dep-${namePrefix}-lb-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ ipConfigurations: [
+ {
+ name: '${serviceShort}01'
+ properties: {
+ primary: true
+ privateIPAllocationMethod: 'Dynamic'
+ subnet: {
+ id: nestedDependencies.outputs.subnetResourceId
+ }
+ }
+ }
+ ]
+ loadBalancerFrontendIpConfigurations: [
+ {
+ id: nestedDependencies.outputs.loadBalancerFrontendIpConfigurationResourceId
+ }
+ ]
+ autoApproval: {
+ subscriptions: [
+ '*'
+ ]
+ }
+ visibility: {
+ subscriptions: [
+ subscription().subscriptionId
+ ]
+ }
+ enableProxyProtocol: true
+ fqdns: [
+ '${serviceShort}.plsfqdn01.azure.privatelinkservice'
+ '${serviceShort}.plsfqdn02.azure.privatelinkservice'
+ ]
+ roleAssignments: [
+ {
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ roleDefinitionIdOrName: 'Reader'
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/public-ip-address/README.md b/modules/network/public-ip-address/README.md
index a1e26a8374..cfe71b8195 100644
--- a/modules/network/public-ip-address/README.md
+++ b/modules/network/public-ip-address/README.md
@@ -31,6 +31,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -216,6 +217,142 @@ module publicIpAddress 'br:bicep/modules/network.public-ip-address:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/public-ip-address/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/public-ip-address/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/network/public-ip-address/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/public-ip-address/tests/e2e/waf-aligned/main.test.bicep b/modules/network/public-ip-address/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..61d5598c0e
--- /dev/null
+++ b/modules/network/public-ip-address/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,107 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.publicipaddresses-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'npiawaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ publicIPAllocationMethod: 'Static'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ skuName: 'Standard'
+ zones: [
+ '1'
+ '2'
+ '3'
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/public-ip-prefix/README.md b/modules/network/public-ip-prefix/README.md
index efd58740b9..315a9026fd 100644
--- a/modules/network/public-ip-prefix/README.md
+++ b/modules/network/public-ip-prefix/README.md
@@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -171,6 +172,96 @@ module publicIpPrefix 'br:bicep/modules/network.public-ip-prefix:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/public-ip-prefix/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/public-ip-prefix/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/network/public-ip-prefix/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/public-ip-prefix/tests/e2e/waf-aligned/main.test.bicep b/modules/network/public-ip-prefix/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..298ddcbc5d
--- /dev/null
+++ b/modules/network/public-ip-prefix/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,73 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.publicipprefixes-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'npipwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ prefixLength: 28
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/route-table/README.md b/modules/network/route-table/README.md
index 3187ab66e4..f5c8ab94de 100644
--- a/modules/network/route-table/README.md
+++ b/modules/network/route-table/README.md
@@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -185,6 +186,114 @@ module routeTable 'br:bicep/modules/network.route-table:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/route-table/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/route-table/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/network/route-table/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/route-table/tests/e2e/waf-aligned/main.test.bicep b/modules/network/route-table/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..83c92c0105
--- /dev/null
+++ b/modules/network/route-table/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,82 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.routetables-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nrtwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ routes: [
+ {
+ name: 'default'
+ properties: {
+ addressPrefix: '0.0.0.0/0'
+ nextHopIpAddress: '172.16.0.20'
+ nextHopType: 'VirtualAppliance'
+ }
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/service-endpoint-policy/README.md b/modules/network/service-endpoint-policy/README.md
index c97f6b3a41..e8797a413c 100644
--- a/modules/network/service-endpoint-policy/README.md
+++ b/modules/network/service-endpoint-policy/README.md
@@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -191,6 +192,120 @@ module serviceEndpointPolicy 'br:bicep/modules/network.service-endpoint-policy:1
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/service-endpoint-policy/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/service-endpoint-policy/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/network/service-endpoint-policy/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/service-endpoint-policy/tests/e2e/waf-aligned/main.test.bicep b/modules/network/service-endpoint-policy/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..f2a407ed2a
--- /dev/null
+++ b/modules/network/service-endpoint-policy/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,85 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.serviceendpointpolicies-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nsnpwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}-${serviceShort}-001'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ serviceEndpointPolicyDefinitions: [
+ {
+ name: 'Storage.ServiceEndpoint'
+ properties: {
+ service: 'Microsoft.Storage'
+ description: 'Allow Microsoft.Storage'
+ serviceResources: [
+ subscription().id
+ ]
+ }
+ type: 'Microsoft.Network/serviceEndpointPolicies/serviceEndpointPolicyDefinitions'
+ }
+ ]
+ }
+}
diff --git a/modules/network/trafficmanagerprofile/README.md b/modules/network/trafficmanagerprofile/README.md
index 07d77ebdb1..01f22925a2 100644
--- a/modules/network/trafficmanagerprofile/README.md
+++ b/modules/network/trafficmanagerprofile/README.md
@@ -30,6 +30,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -203,6 +204,126 @@ module trafficmanagerprofile 'br:bicep/modules/network.trafficmanagerprofile:1.0
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/trafficmanagerprofile/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/trafficmanagerprofile/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/network/trafficmanagerprofile/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/trafficmanagerprofile/tests/e2e/waf-aligned/main.test.bicep b/modules/network/trafficmanagerprofile/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..a1a7cb5738
--- /dev/null
+++ b/modules/network/trafficmanagerprofile/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,101 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.trafficmanagerprofiles-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'ntmpwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+var resourceName = '${namePrefix}${serviceShort}001'
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: resourceName
+ relativeName: resourceName
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/virtual-hub/README.md b/modules/network/virtual-hub/README.md
index 794271f0ac..c4c25d0839 100644
--- a/modules/network/virtual-hub/README.md
+++ b/modules/network/virtual-hub/README.md
@@ -30,6 +30,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -221,6 +222,140 @@ module virtualHub 'br:bicep/modules/network.virtual-hub:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/virtual-hub/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/virtual-hub/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..9c4af5313d
--- /dev/null
+++ b/modules/network/virtual-hub/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,42 @@
+@description('Required. The name of the Virtual WAN to create.')
+param virtualWANName string
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualWan 'Microsoft.Network/virtualWans@2023-04-01' = {
+ name: virtualWANName
+ location: location
+}
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ }
+ }
+ ]
+ }
+}
+
+@description('The resource ID of the created Virtual WAN.')
+output virtualWWANResourceId string = virtualWan.id
+
+@description('The resource ID of the created Virtual Network.')
+output virtualNetworkResourceId string = virtualNetwork.id
diff --git a/modules/network/virtual-hub/tests/e2e/waf-aligned/main.test.bicep b/modules/network/virtual-hub/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..8ca1b21cbd
--- /dev/null
+++ b/modules/network/virtual-hub/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,94 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.virtualHub-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nvhwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualWANName: 'dep-${namePrefix}-vw-${serviceShort}'
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}-${serviceShort}'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ addressPrefix: '10.1.0.0/16'
+ virtualWanId: nestedDependencies.outputs.virtualWWANResourceId
+ hubRouteTables: [
+ {
+ name: 'routeTable1'
+ }
+ ]
+ hubVirtualNetworkConnections: [
+ {
+ name: 'connection1'
+ remoteVirtualNetworkId: nestedDependencies.outputs.virtualNetworkResourceId
+ routingConfiguration: {
+ associatedRouteTable: {
+ id: '${resourceGroup.id}/providers/Microsoft.Network/virtualHubs/${namePrefix}-${serviceShort}/hubRouteTables/routeTable1'
+ }
+ propagatedRouteTables: {
+ ids: [
+ {
+ id: '${resourceGroup.id}/providers/Microsoft.Network/virtualHubs/${namePrefix}-${serviceShort}/hubRouteTables/routeTable1'
+ }
+ ]
+ labels: [
+ 'none'
+ ]
+ }
+ }
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/virtual-network/README.md b/modules/network/virtual-network/README.md
index 07083c6cf7..8f8acb2d0d 100644
--- a/modules/network/virtual-network/README.md
+++ b/modules/network/virtual-network/README.md
@@ -33,6 +33,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
- [Vnetpeering](#example-3-vnetpeering)
+- [WAF-aligned](#example-4-waf-aligned)
### Example 1: _Using only defaults_
@@ -427,6 +428,236 @@ module virtualNetwork 'br:bicep/modules/network.virtual-network:1.0.0' = {
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/virtual-network/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/virtual-network/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..065c08da1e
--- /dev/null
+++ b/modules/network/virtual-network/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,35 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the Route Table to create.')
+param routeTableName string
+
+@description('Required. The name of the Network Security Group to create.')
+param networkSecurityGroupName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource routeTable 'Microsoft.Network/routeTables@2023-04-01' = {
+ name: routeTableName
+ location: location
+}
+
+resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = {
+ name: networkSecurityGroupName
+ location: location
+}
+
+@description('The resource ID of the created Route Table.')
+output routeTableResourceId string = routeTable.id
+
+@description('The resource ID of the created Network Security Group.')
+output networkSecurityGroupResourceId string = networkSecurityGroup.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/virtual-network/tests/e2e/waf-aligned/main.test.bicep b/modules/network/virtual-network/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..58a38a9530
--- /dev/null
+++ b/modules/network/virtual-network/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,156 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.virtualnetworks-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nvnwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ routeTableName: 'dep-${namePrefix}-rt-${serviceShort}'
+ networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+var addressPrefix = '10.0.0.0/16'
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ addressPrefixes: [
+ addressPrefix
+ ]
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ dnsServers: [
+ '10.0.1.4'
+ '10.0.1.5'
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ flowTimeoutInMinutes: 20
+ subnets: [
+ {
+ addressPrefix: cidrSubnet(addressPrefix, 24, 0)
+ name: 'GatewaySubnet'
+ }
+ {
+ addressPrefix: cidrSubnet(addressPrefix, 24, 1)
+ name: '${namePrefix}-az-subnet-x-001'
+ networkSecurityGroupId: nestedDependencies.outputs.networkSecurityGroupResourceId
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ routeTableId: nestedDependencies.outputs.routeTableResourceId
+ serviceEndpoints: [
+ {
+ service: 'Microsoft.Storage'
+ }
+ {
+ service: 'Microsoft.Sql'
+ }
+ ]
+ }
+ {
+ addressPrefix: cidrSubnet(addressPrefix, 24, 2)
+ delegations: [
+ {
+ name: 'netappDel'
+ properties: {
+ serviceName: 'Microsoft.Netapp/volumes'
+ }
+ }
+ ]
+ name: '${namePrefix}-az-subnet-x-002'
+ }
+ {
+ addressPrefix: cidrSubnet(addressPrefix, 24, 3)
+ name: '${namePrefix}-az-subnet-x-003'
+ privateEndpointNetworkPolicies: 'Disabled'
+ privateLinkServiceNetworkPolicies: 'Enabled'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/virtual-wan/README.md b/modules/network/virtual-wan/README.md
index 2837b5d97c..63d33bf1aa 100644
--- a/modules/network/virtual-wan/README.md
+++ b/modules/network/virtual-wan/README.md
@@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -179,6 +180,108 @@ module virtualWan 'br:bicep/modules/network.virtual-wan:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/virtual-wan/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/virtual-wan/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/network/virtual-wan/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/network/virtual-wan/tests/e2e/waf-aligned/main.test.bicep b/modules/network/virtual-wan/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..748fcbeaac
--- /dev/null
+++ b/modules/network/virtual-wan/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,76 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.virtualwans-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nvwwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ allowBranchToBranchTraffic: true
+ allowVnetToVnetTraffic: true
+ disableVpnEncryption: true
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ type: 'Basic'
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/vpn-gateway/README.md b/modules/network/vpn-gateway/README.md
index e8936ad31c..ae23f37365 100644
--- a/modules/network/vpn-gateway/README.md
+++ b/modules/network/vpn-gateway/README.md
@@ -30,6 +30,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -233,6 +234,156 @@ module vpnGateway 'br:bicep/modules/network.vpn-gateway:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/vpn-gateway/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/vpn-gateway/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a15b268388
--- /dev/null
+++ b/modules/network/vpn-gateway/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,49 @@
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Optional. The name of the Virtual Hub to create.')
+param virtualHubName string
+
+@description('Optional. The name of the VPN Site to create.')
+param vpnSiteName string
+
+@description('Required. The name of the virtual WAN to create.')
+param virtualWANName string
+
+resource virtualWan 'Microsoft.Network/virtualWans@2023-04-01' = {
+ name: virtualWANName
+ location: location
+}
+
+resource virtualHub 'Microsoft.Network/virtualHubs@2022-01-01' = {
+ name: virtualHubName
+ location: location
+ properties: {
+ virtualWan: {
+ id: virtualWan.id
+ }
+ addressPrefix: '10.0.0.0/24'
+ }
+}
+
+resource vpnSite 'Microsoft.Network/vpnSites@2023-04-01' = {
+ name: vpnSiteName
+ location: location
+ properties: {
+ virtualWan: {
+ id: virtualWan.id
+ }
+ addressSpace: {
+ addressPrefixes: [
+ '10.1.0.0/16'
+ ]
+ }
+ ipAddress: '10.1.0.0'
+ }
+}
+
+@description('The resource ID of the created Virtual Hub.')
+output virtualHubResourceId string = virtualHub.id
+
+@description('The resource ID of the created VPN site.')
+output vpnSiteResourceId string = vpnSite.id
diff --git a/modules/network/vpn-gateway/tests/e2e/waf-aligned/main.test.bicep b/modules/network/vpn-gateway/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..7d7999ab09
--- /dev/null
+++ b/modules/network/vpn-gateway/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,102 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.vpngateways-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nvgwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualHubName: 'dep-${namePrefix}-vh-${serviceShort}'
+ virtualWANName: 'dep-${namePrefix}-vw-${serviceShort}'
+ vpnSiteName: 'dep-${namePrefix}-vs-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ virtualHubResourceId: nestedDependencies.outputs.virtualHubResourceId
+ bgpSettings: {
+ asn: 65515
+ peerWeight: 0
+ }
+ vpnConnections: [
+ {
+ connectionBandwidth: 100
+ enableBgp: false
+ name: 'Connection-${last(split(nestedDependencies.outputs.vpnSiteResourceId, '/'))}'
+ remoteVpnSiteResourceId: nestedDependencies.outputs.vpnSiteResourceId
+ enableInternetSecurity: true
+ vpnConnectionProtocolType: 'IKEv2'
+ enableRateLimiting: false
+ useLocalAzureIpAddress: false
+ usePolicyBasedTrafficSelectors: false
+ routingWeight: 0
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ natRules: [
+ {
+ externalMappings: [
+ {
+ addressSpace: '192.168.21.0/24'
+ }
+ ]
+ internalMappings: [
+ {
+ addressSpace: '10.4.0.0/24'
+ }
+ ]
+ mode: 'EgressSnat'
+ name: 'natRule1'
+ type: 'Static'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/network/vpn-site/README.md b/modules/network/vpn-site/README.md
index 949d02fd41..0db53524cd 100644
--- a/modules/network/vpn-site/README.md
+++ b/modules/network/vpn-site/README.md
@@ -29,6 +29,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -270,6 +271,182 @@ module vpnSite 'br:bicep/modules/network.vpn-site:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/network/vpn-site/tests/e2e/waf-aligned/dependencies.bicep b/modules/network/vpn-site/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..8e2694c27f
--- /dev/null
+++ b/modules/network/vpn-site/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,24 @@
+@description('Required. The name of the managed identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the virtual WAN to create.')
+param virtualWANName string
+
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource virtualWan 'Microsoft.Network/virtualWans@2023-04-01' = {
+ name: virtualWANName
+ location: location
+}
+
+@description('The principal ID of the created managed identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Virtual WAN.')
+output virtualWWANResourceId string = virtualWan.id
diff --git a/modules/network/vpn-site/tests/e2e/waf-aligned/main.test.bicep b/modules/network/vpn-site/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..66ea85793c
--- /dev/null
+++ b/modules/network/vpn-site/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,114 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.vpnSites-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nvswaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ virtualWANName: 'dep-${namePrefix}-vw-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}-${serviceShort}'
+ virtualWanId: nestedDependencies.outputs.virtualWWANResourceId
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ tagA: 'valueA'
+ tagB: 'valueB'
+ }
+ deviceProperties: {
+ linkSpeedInMbps: 0
+ }
+ vpnSiteLinks: [
+ {
+ name: '${namePrefix}-vSite-${serviceShort}'
+ properties: {
+ bgpProperties: {
+ asn: 65010
+ bgpPeeringAddress: '1.1.1.1'
+ }
+ ipAddress: '1.2.3.4'
+ linkProperties: {
+ linkProviderName: 'contoso'
+ linkSpeedInMbps: 5
+ }
+ }
+ }
+ {
+ name: 'Link1'
+ properties: {
+ bgpProperties: {
+ asn: 65020
+ bgpPeeringAddress: '192.168.1.0'
+ }
+ ipAddress: '2.2.2.2'
+ linkProperties: {
+ linkProviderName: 'contoso'
+ linkSpeedInMbps: 5
+ }
+ }
+ }
+ ]
+ o365Policy: {
+ breakOutCategories: {
+ optimize: true
+ allow: true
+ default: true
+ }
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ }
+}
diff --git a/modules/operational-insights/workspace/README.md b/modules/operational-insights/workspace/README.md
index cac8424e47..ec2727000b 100644
--- a/modules/operational-insights/workspace/README.md
+++ b/modules/operational-insights/workspace/README.md
@@ -38,6 +38,7 @@ The following section provides usage examples for the module, which were used to
- [Adv](#example-1-adv)
- [Using only defaults](#example-2-using-only-defaults)
- [Using large parameter set](#example-3-using-large-parameter-set)
+- [WAF-aligned](#example-4-waf-aligned)
### Example 1: _Adv_
@@ -1048,6 +1049,414 @@ module workspace 'br:bicep/modules/operational-insights.workspace:1.0.0' = {
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/operational-insights/workspace/tests/e2e/waf-aligned/dependencies.bicep b/modules/operational-insights/workspace/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..8f83c0d9a1
--- /dev/null
+++ b/modules/operational-insights/workspace/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,47 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Storage Account to create.')
+param storageAccountName string
+
+@description('Required. The name of the Automation Account to create.')
+param automationAccountName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {
+ name: storageAccountName
+ location: location
+ sku: {
+ name: 'Standard_LRS'
+ }
+ kind: 'StorageV2'
+}
+
+resource automationAccount 'Microsoft.Automation/automationAccounts@2022-08-08' = {
+ name: automationAccountName
+ location: location
+ properties: {
+ sku: {
+ name: 'Basic'
+ }
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Storage Account.')
+output storageAccountResourceId string = storageAccount.id
+
+@description('The resource ID of the created Automation Account.')
+output automationAccountResourceId string = automationAccount.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
diff --git a/modules/operational-insights/workspace/tests/e2e/waf-aligned/main.test.bicep b/modules/operational-insights/workspace/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..92f24e5733
--- /dev/null
+++ b/modules/operational-insights/workspace/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,237 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-operationalinsights.workspaces-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'oiwwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}sa${serviceShort}'
+ automationAccountName: 'dep-${namePrefix}-auto-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ dailyQuotaGb: 10
+ dataSources: [
+ {
+ eventLogName: 'Application'
+ eventTypes: [
+ {
+ eventType: 'Error'
+ }
+ {
+ eventType: 'Warning'
+ }
+ {
+ eventType: 'Information'
+ }
+ ]
+ kind: 'WindowsEvent'
+ name: 'applicationEvent'
+ }
+ {
+ counterName: '% Processor Time'
+ instanceName: '*'
+ intervalSeconds: 60
+ kind: 'WindowsPerformanceCounter'
+ name: 'windowsPerfCounter1'
+ objectName: 'Processor'
+ }
+ {
+ kind: 'IISLogs'
+ name: 'sampleIISLog1'
+ state: 'OnPremiseEnabled'
+ }
+ {
+ kind: 'LinuxSyslog'
+ name: 'sampleSyslog1'
+ syslogName: 'kern'
+ syslogSeverities: [
+ {
+ severity: 'emerg'
+ }
+ {
+ severity: 'alert'
+ }
+ {
+ severity: 'crit'
+ }
+ {
+ severity: 'err'
+ }
+ {
+ severity: 'warning'
+ }
+ ]
+ }
+ {
+ kind: 'LinuxSyslogCollection'
+ name: 'sampleSyslogCollection1'
+ state: 'Enabled'
+ }
+ {
+ instanceName: '*'
+ intervalSeconds: 10
+ kind: 'LinuxPerformanceObject'
+ name: 'sampleLinuxPerf1'
+ objectName: 'Logical Disk'
+ syslogSeverities: [
+ {
+ counterName: '% Used Inodes'
+ }
+ {
+ counterName: 'Free Megabytes'
+ }
+ {
+ counterName: '% Used Space'
+ }
+ {
+ counterName: 'Disk Transfers/sec'
+ }
+ {
+ counterName: 'Disk Reads/sec'
+ }
+ {
+ counterName: 'Disk Writes/sec'
+ }
+ ]
+ }
+ {
+ kind: 'LinuxPerformanceCollection'
+ name: 'sampleLinuxPerfCollection1'
+ state: 'Enabled'
+ }
+ ]
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ gallerySolutions: [
+ {
+ name: 'AzureAutomation'
+ product: 'OMSGallery'
+ publisher: 'Microsoft'
+ }
+ ]
+ linkedServices: [
+ {
+ name: 'Automation'
+ resourceId: nestedDependencies.outputs.automationAccountResourceId
+ }
+ ]
+ linkedStorageAccounts: [
+ {
+ name: 'Query'
+ resourceId: nestedDependencies.outputs.storageAccountResourceId
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ publicNetworkAccessForIngestion: 'Disabled'
+ publicNetworkAccessForQuery: 'Disabled'
+ savedSearches: [
+ {
+ category: 'VDC Saved Searches'
+ displayName: 'VMSS Instance Count2'
+ name: 'VMSSQueries'
+ query: 'Event | where Source == ServiceFabricNodeBootstrapAgent | summarize AggregatedValue = count() by Computer'
+ }
+ ]
+ storageInsightsConfigs: [
+ {
+ storageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId
+ tables: [
+ 'LinuxsyslogVer2v0'
+ 'WADETWEventTable'
+ 'WADServiceFabric*EventTable'
+ 'WADWindowsEventLogsTable'
+ ]
+ }
+ ]
+ useResourcePermissions: true
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ managedIdentities: {
+ systemAssigned: true
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ }
+}
diff --git a/modules/power-bi-dedicated/capacity/README.md b/modules/power-bi-dedicated/capacity/README.md
index 4e238f87bd..93a0348544 100644
--- a/modules/power-bi-dedicated/capacity/README.md
+++ b/modules/power-bi-dedicated/capacity/README.md
@@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -187,6 +188,104 @@ module capacity 'br:bicep/modules/power-bi-dedicated.capacity:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/power-bi-dedicated/capacity/tests/e2e/waf-aligned/dependencies.bicep b/modules/power-bi-dedicated/capacity/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/power-bi-dedicated/capacity/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/power-bi-dedicated/capacity/tests/e2e/waf-aligned/main.test.bicep b/modules/power-bi-dedicated/capacity/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..204d4c8d00
--- /dev/null
+++ b/modules/power-bi-dedicated/capacity/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,76 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-powerbidedicated.capacities-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'pbdcapwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ skuCapacity: 1
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ members: [
+ nestedDependencies.outputs.managedIdentityPrincipalId
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/purview/account/README.md b/modules/purview/account/README.md
index 0110965dca..bf1e13c412 100644
--- a/modules/purview/account/README.md
+++ b/modules/purview/account/README.md
@@ -31,6 +31,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -374,6 +375,296 @@ module account 'br:bicep/modules/purview.account:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/purview/account/tests/e2e/waf-aligned/dependencies.bicep b/modules/purview/account/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..1edeb81930
--- /dev/null
+++ b/modules/purview/account/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,73 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+var addressPrefix = '10.0.0.0/16'
+
+var privateDNSZoneNames = [
+ 'privatelink.purview.azure.com'
+ 'privatelink.purviewstudio.azure.com'
+ 'privatelink.blob.${environment().suffixes.storage}'
+ 'privatelink.queue.${environment().suffixes.storage}'
+ 'privatelink.servicebus.windows.net'
+]
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ }
+ }
+ ]
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@batchSize(1)
+resource privateDNSZones 'Microsoft.Network/privateDnsZones@2020-06-01' = [for privateDNSZone in privateDNSZoneNames: {
+ name: privateDNSZone
+ location: 'global'
+}]
+
+@description('The resource ID of the created Virtual Network Subnet.')
+output subnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
+
+@description('The resource ID of the created Private DNS Zone for Purview Account.')
+output purviewAccountPrivateDNSResourceId string = privateDNSZones[0].id
+
+@description('The resource ID of the created Private DNS Zone for Purview Portal.')
+output purviewPortalPrivateDNSResourceId string = privateDNSZones[1].id
+
+@description('The resource ID of the created Private DNS Zone for Storage Account Blob.')
+output storageBlobPrivateDNSResourceId string = privateDNSZones[2].id
+
+@description('The resource ID of the created Private DNS Zone for Storage Account Queue.')
+output storageQueuePrivateDNSResourceId string = privateDNSZones[3].id
+
+@description('The resource ID of the created Private DNS Zone for Event Hub Namespace.')
+output eventHubPrivateDNSResourceId string = privateDNSZones[4].id
diff --git a/modules/purview/account/tests/e2e/waf-aligned/main.test.bicep b/modules/purview/account/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..1fc2ee5e43
--- /dev/null
+++ b/modules/purview/account/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,179 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-purview-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = 'eastus' // Only available in selected locations: eastus, eastus2, southcentralus, westcentralus, westus, westus2, westus3
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'pvawaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+// =========== //
+// Deployments //
+// =========== //
+
+// General resources
+// =================
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}01'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}01'
+ location: location
+
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name)}-test-${serviceShort}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ location: location
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ managedIdentities: {
+ userAssignedResourcesIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ managedResourceGroupName: '${namePrefix}${serviceShort}001-managed-rg'
+ publicNetworkAccess: 'Disabled'
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ accountPrivateEndpoints: [
+ {
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.purviewAccountPrivateDNSResourceId
+ ]
+ service: 'account'
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ portalPrivateEndpoints: [
+ {
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.purviewPortalPrivateDNSResourceId
+ ]
+ service: 'portal'
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ storageBlobPrivateEndpoints: [
+ {
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.storageBlobPrivateDNSResourceId
+ ]
+ service: 'blob'
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ storageQueuePrivateEndpoints: [
+ {
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.storageQueuePrivateDNSResourceId
+ ]
+ service: 'queue'
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ eventHubPrivateEndpoints: [
+ {
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.eventHubPrivateDNSResourceId
+ ]
+ service: 'namespace'
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ enableDefaultTelemetry: enableDefaultTelemetry
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ }
+}
diff --git a/modules/recovery-services/vault/README.md b/modules/recovery-services/vault/README.md
index 0f801f9e45..6543c19403 100644
--- a/modules/recovery-services/vault/README.md
+++ b/modules/recovery-services/vault/README.md
@@ -42,6 +42,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Dr](#example-2-dr)
- [Using large parameter set](#example-3-using-large-parameter-set)
+- [WAF-aligned](#example-4-waf-aligned)
### Example 1: _Using only defaults_
@@ -948,6 +949,692 @@ module vault 'br:bicep/modules/recovery-services.vault:1.0.0' = {
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/recovery-services/vault/tests/e2e/waf-aligned/dependencies.bicep b/modules/recovery-services/vault/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..12b8653f54
--- /dev/null
+++ b/modules/recovery-services/vault/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,63 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ }
+ }
+ ]
+ }
+}
+
+resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
+ name: 'privatelink.siterecovery.windowsazure.com'
+ location: 'global'
+
+ resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = {
+ name: '${virtualNetwork.name}-vnetlink'
+ location: 'global'
+ properties: {
+ virtualNetwork: {
+ id: virtualNetwork.id
+ }
+ registrationEnabled: false
+ }
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Virtual Network Subnet.')
+output subnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
+
+@description('The resource ID of the created Private DNS Zone.')
+output privateDNSZoneResourceId string = privateDNSZone.id
diff --git a/modules/recovery-services/vault/tests/e2e/waf-aligned/main.test.bicep b/modules/recovery-services/vault/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..c61f06f157
--- /dev/null
+++ b/modules/recovery-services/vault/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,378 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-recoveryservices.vaults-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'rsvwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ backupConfig: {
+ enhancedSecurityState: 'Disabled'
+ softDeleteFeatureState: 'Disabled'
+ }
+ backupPolicies: [
+ {
+ name: 'VMpolicy'
+ properties: {
+ backupManagementType: 'AzureIaasVM'
+ instantRPDetails: {}
+ instantRpRetentionRangeInDays: 2
+ protectedItemsCount: 0
+ retentionPolicy: {
+ dailySchedule: {
+ retentionDuration: {
+ count: 180
+ durationType: 'Days'
+ }
+ retentionTimes: [
+ '2019-11-07T07:00:00Z'
+ ]
+ }
+ monthlySchedule: {
+ retentionDuration: {
+ count: 60
+ durationType: 'Months'
+ }
+ retentionScheduleFormatType: 'Weekly'
+ retentionScheduleWeekly: {
+ daysOfTheWeek: [
+ 'Sunday'
+ ]
+ weeksOfTheMonth: [
+ 'First'
+ ]
+ }
+ retentionTimes: [
+ '2019-11-07T07:00:00Z'
+ ]
+ }
+ retentionPolicyType: 'LongTermRetentionPolicy'
+ weeklySchedule: {
+ daysOfTheWeek: [
+ 'Sunday'
+ ]
+ retentionDuration: {
+ count: 12
+ durationType: 'Weeks'
+ }
+ retentionTimes: [
+ '2019-11-07T07:00:00Z'
+ ]
+ }
+ yearlySchedule: {
+ monthsOfYear: [
+ 'January'
+ ]
+ retentionDuration: {
+ count: 10
+ durationType: 'Years'
+ }
+ retentionScheduleFormatType: 'Weekly'
+ retentionScheduleWeekly: {
+ daysOfTheWeek: [
+ 'Sunday'
+ ]
+ weeksOfTheMonth: [
+ 'First'
+ ]
+ }
+ retentionTimes: [
+ '2019-11-07T07:00:00Z'
+ ]
+ }
+ }
+ schedulePolicy: {
+ schedulePolicyType: 'SimpleSchedulePolicy'
+ scheduleRunFrequency: 'Daily'
+ scheduleRunTimes: [
+ '2019-11-07T07:00:00Z'
+ ]
+ scheduleWeeklyFrequency: 0
+ }
+ timeZone: 'UTC'
+ }
+ }
+ {
+ name: 'sqlpolicy'
+ properties: {
+ backupManagementType: 'AzureWorkload'
+ protectedItemsCount: 0
+ settings: {
+ isCompression: true
+ issqlcompression: true
+ timeZone: 'UTC'
+ }
+ subProtectionPolicy: [
+ {
+ policyType: 'Full'
+ retentionPolicy: {
+ monthlySchedule: {
+ retentionDuration: {
+ count: 60
+ durationType: 'Months'
+ }
+ retentionScheduleFormatType: 'Weekly'
+ retentionScheduleWeekly: {
+ daysOfTheWeek: [
+ 'Sunday'
+ ]
+ weeksOfTheMonth: [
+ 'First'
+ ]
+ }
+ retentionTimes: [
+ '2019-11-07T22:00:00Z'
+ ]
+ }
+ retentionPolicyType: 'LongTermRetentionPolicy'
+ weeklySchedule: {
+ daysOfTheWeek: [
+ 'Sunday'
+ ]
+ retentionDuration: {
+ count: 104
+ durationType: 'Weeks'
+ }
+ retentionTimes: [
+ '2019-11-07T22:00:00Z'
+ ]
+ }
+ yearlySchedule: {
+ monthsOfYear: [
+ 'January'
+ ]
+ retentionDuration: {
+ count: 10
+ durationType: 'Years'
+ }
+ retentionScheduleFormatType: 'Weekly'
+ retentionScheduleWeekly: {
+ daysOfTheWeek: [
+ 'Sunday'
+ ]
+ weeksOfTheMonth: [
+ 'First'
+ ]
+ }
+ retentionTimes: [
+ '2019-11-07T22:00:00Z'
+ ]
+ }
+ }
+ schedulePolicy: {
+ schedulePolicyType: 'SimpleSchedulePolicy'
+ scheduleRunDays: [
+ 'Sunday'
+ ]
+ scheduleRunFrequency: 'Weekly'
+ scheduleRunTimes: [
+ '2019-11-07T22:00:00Z'
+ ]
+ scheduleWeeklyFrequency: 0
+ }
+ }
+ {
+ policyType: 'Differential'
+ retentionPolicy: {
+ retentionDuration: {
+ count: 30
+ durationType: 'Days'
+ }
+ retentionPolicyType: 'SimpleRetentionPolicy'
+ }
+ schedulePolicy: {
+ schedulePolicyType: 'SimpleSchedulePolicy'
+ scheduleRunDays: [
+ 'Monday'
+ ]
+ scheduleRunFrequency: 'Weekly'
+ scheduleRunTimes: [
+ '2017-03-07T02:00:00Z'
+ ]
+ scheduleWeeklyFrequency: 0
+ }
+ }
+ {
+ policyType: 'Log'
+ retentionPolicy: {
+ retentionDuration: {
+ count: 15
+ durationType: 'Days'
+ }
+ retentionPolicyType: 'SimpleRetentionPolicy'
+ }
+ schedulePolicy: {
+ scheduleFrequencyInMins: 120
+ schedulePolicyType: 'LogSchedulePolicy'
+ }
+ }
+ ]
+ workLoadType: 'SQLDataBase'
+ }
+ }
+ {
+ name: 'filesharepolicy'
+ properties: {
+ backupManagementType: 'AzureStorage'
+ protectedItemsCount: 0
+ retentionPolicy: {
+ dailySchedule: {
+ retentionDuration: {
+ count: 30
+ durationType: 'Days'
+ }
+ retentionTimes: [
+ '2019-11-07T04:30:00Z'
+ ]
+ }
+ retentionPolicyType: 'LongTermRetentionPolicy'
+ }
+ schedulePolicy: {
+ schedulePolicyType: 'SimpleSchedulePolicy'
+ scheduleRunFrequency: 'Daily'
+ scheduleRunTimes: [
+ '2019-11-07T04:30:00Z'
+ ]
+ scheduleWeeklyFrequency: 0
+ }
+ timeZone: 'UTC'
+ workloadType: 'AzureFileShare'
+ }
+ }
+ ]
+ backupStorageConfig: {
+ crossRegionRestoreFlag: true
+ storageModelType: 'GeoRedundant'
+ }
+ replicationAlertSettings: {
+ customEmailAddresses: [
+ 'test.user@testcompany.com'
+ ]
+ locale: 'en-US'
+ sendToOwners: 'Send'
+ }
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ managedIdentities: {
+ systemAssigned: true
+ userAssignedResourcesIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ privateEndpoints: [
+ {
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.privateDNSZoneResourceId
+ ]
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ monitoringSettings: {
+ azureMonitorAlertSettings: {
+ alertsForAllJobFailures: 'Enabled'
+ }
+ classicAlertSettings: {
+ alertsForCriticalOperations: 'Enabled'
+ }
+ }
+ securitySettings: {
+ immutabilitySettings: {
+ state: 'Unlocked'
+ }
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/relay/namespace/README.md b/modules/relay/namespace/README.md
index f7f4a331ec..0e0ec1776b 100644
--- a/modules/relay/namespace/README.md
+++ b/modules/relay/namespace/README.md
@@ -38,6 +38,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
- [Pe](#example-3-pe)
+- [WAF-aligned](#example-4-waf-aligned)
### Example 1: _Using only defaults_
@@ -464,6 +465,294 @@ module namespace 'br:bicep/modules/relay.namespace:1.0.0' = {
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/relay/namespace/tests/e2e/waf-aligned/dependencies.bicep b/modules/relay/namespace/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..cf1b2ab392
--- /dev/null
+++ b/modules/relay/namespace/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,60 @@
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ }
+ }
+ ]
+ }
+}
+
+resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
+ name: 'privatelink.servicebus.windows.net'
+ location: 'global'
+
+ resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = {
+ name: '${virtualNetwork.name}-vnetlink'
+ location: 'global'
+ properties: {
+ virtualNetwork: {
+ id: virtualNetwork.id
+ }
+ registrationEnabled: false
+ }
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Virtual Network Subnet.')
+output subnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Private DNS Zone.')
+output privateDNSZoneResourceId string = privateDNSZone.id
diff --git a/modules/relay/namespace/tests/e2e/waf-aligned/main.test.bicep b/modules/relay/namespace/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..2e3268af07
--- /dev/null
+++ b/modules/relay/namespace/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,181 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-relay.namespaces-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'rnwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ skuName: 'Standard'
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ networkRuleSets: {
+ defaultAction: 'Deny'
+ trustedServiceAccessEnabled: true
+ virtualNetworkRules: [
+ {
+ subnet: {
+ ignoreMissingVnetServiceEndpoint: true
+ id: nestedDependencies.outputs.subnetResourceId
+ }
+ }
+ ]
+ ipRules: [
+ {
+ ipMask: '10.0.1.0/32'
+ action: 'Allow'
+ }
+ {
+ ipMask: '10.0.2.0/32'
+ action: 'Allow'
+ }
+ ]
+ }
+ authorizationRules: [
+ {
+ name: 'RootManageSharedAccessKey'
+ rights: [
+ 'Listen'
+ 'Manage'
+ 'Send'
+ ]
+ }
+ {
+ name: 'AnotherKey'
+ rights: [
+ 'Listen'
+ 'Send'
+ ]
+ }
+ ]
+ hybridConnections: [
+ {
+ name: '${namePrefix}${serviceShort}hc001'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ userMetadata: '[{"key":"endpoint","value":"db-server.constoso.com:1433"}]'
+ }
+ ]
+ wcfRelays: [
+ {
+ name: '${namePrefix}${serviceShort}wcf001'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ relayType: 'NetTcp'
+ }
+ ]
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ privateEndpoints: [
+ {
+ service: 'namespace'
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.privateDNSZoneResourceId
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ }
+}
diff --git a/modules/resource-graph/query/README.md b/modules/resource-graph/query/README.md
index b0a81c470e..b9d4187d55 100644
--- a/modules/resource-graph/query/README.md
+++ b/modules/resource-graph/query/README.md
@@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -175,6 +176,100 @@ module query 'br:bicep/modules/resource-graph.query:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/resource-graph/query/tests/e2e/waf-aligned/dependencies.bicep b/modules/resource-graph/query/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/resource-graph/query/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/resource-graph/query/tests/e2e/waf-aligned/main.test.bicep b/modules/resource-graph/query/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..5858166d43
--- /dev/null
+++ b/modules/resource-graph/query/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,74 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-resourcegraph.queries-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'rgqwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ query: 'resources | take 10'
+ queryDescription: 'An example query to list first 10 resources in the subscription.'
+ }
+}
diff --git a/modules/resources/resource-group/README.md b/modules/resources/resource-group/README.md
index e80ab43762..6e0fab2365 100644
--- a/modules/resources/resource-group/README.md
+++ b/modules/resources/resource-group/README.md
@@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -163,6 +164,92 @@ module resourceGroup 'br:bicep/modules/resources.resource-group:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/resources/resource-group/tests/e2e/waf-aligned/dependencies.bicep b/modules/resources/resource-group/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..8d9be85388
--- /dev/null
+++ b/modules/resources/resource-group/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,17 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+ tags: {
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/resources/resource-group/tests/e2e/waf-aligned/main.test.bicep b/modules/resources/resource-group/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..d5e6d7df88
--- /dev/null
+++ b/modules/resources/resource-group/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,71 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-resources.resourcegroups-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'rrgwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/search/search-service/README.md b/modules/search/search-service/README.md
index 3a6fe2f628..94d3e8eeff 100644
--- a/modules/search/search-service/README.md
+++ b/modules/search/search-service/README.md
@@ -33,6 +33,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
- [Pe](#example-3-pe)
+- [WAF-aligned](#example-4-waf-aligned)
### Example 1: _Using only defaults_
@@ -395,6 +396,198 @@ module searchService 'br:bicep/modules/search.search-service:1.0.0' = {
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/search/search-service/tests/e2e/waf-aligned/dependencies.bicep b/modules/search/search-service/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..8413dfd20e
--- /dev/null
+++ b/modules/search/search-service/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Required. The name of the managed identity to create.')
+param managedIdentityName string
+
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/search/search-service/tests/e2e/waf-aligned/main.test.bicep b/modules/search/search-service/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..c01e840d45
--- /dev/null
+++ b/modules/search/search-service/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,129 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-search.searchservices-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'ssswaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}03'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}01'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}01'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ sku: 'standard3'
+ cmkEnforcement: 'Enabled'
+ disableLocalAuth: false
+ authOptions: {
+ aadOrApiKey: {
+ aadAuthFailureMode: 'http401WithBearerChallenge'
+ }
+ }
+ hostingMode: 'highDensity'
+ partitionCount: 2
+ replicaCount: 3
+ managedIdentities: {
+ systemAssigned: true
+ }
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ {
+ roleDefinitionIdOrName: 'Search Service Contributor'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ networkRuleSet: {
+ ipRules: [
+ {
+ value: '40.74.28.0/23'
+ }
+ {
+ value: '87.147.204.13'
+ }
+ ]
+ }
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/security/azure-security-center/README.md b/modules/security/azure-security-center/README.md
index ea0247aee2..f3a67e036f 100644
--- a/modules/security/azure-security-center/README.md
+++ b/modules/security/azure-security-center/README.md
@@ -30,6 +30,7 @@ The following section provides usage examples for the module, which were used to
>**Note**: To reference the module, please use the following syntax `br:bicep/modules/security.azure-security-center:1.0.0`.
- [Using large parameter set](#example-1-using-large-parameter-set)
+- [WAF-aligned](#example-2-waf-aligned)
### Example 1: _Using large parameter set_
@@ -93,6 +94,68 @@ module azureSecurityCenter 'br:bicep/modules/security.azure-security-center:1.0.
+### Example 2: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/security/azure-security-center/tests/e2e/waf-aligned/dependencies.bicep b/modules/security/azure-security-center/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..cc24476629
--- /dev/null
+++ b/modules/security/azure-security-center/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Log Analytics Workspace to create.')
+param logAnalyticsWorkspaceName string
+
+resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
+ name: logAnalyticsWorkspaceName
+ location: location
+}
+
+@description('The resource ID of the created Log Analytics Workspace.')
+output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id
diff --git a/modules/security/azure-security-center/tests/e2e/waf-aligned/main.test.bicep b/modules/security/azure-security-center/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..1bb6ec0985
--- /dev/null
+++ b/modules/security/azure-security-center/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,62 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-security.azureSecurityCenter-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'sascwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ scope: '/subscriptions/${subscription().subscriptionId}'
+ workspaceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId
+ securityContactProperties: {
+ alertNotifications: 'Off'
+ alertsToAdmins: 'Off'
+ email: 'foo@contoso.com'
+ phone: '+12345678'
+ }
+ }
+}
diff --git a/modules/service-bus/namespace/README.md b/modules/service-bus/namespace/README.md
index 60aef288fd..db6e405643 100644
--- a/modules/service-bus/namespace/README.md
+++ b/modules/service-bus/namespace/README.md
@@ -41,6 +41,7 @@ The following section provides usage examples for the module, which were used to
- [Encr](#example-2-encr)
- [Using large parameter set](#example-3-using-large-parameter-set)
- [Pe](#example-4-pe)
+- [WAF-aligned](#example-5-waf-aligned)
### Example 1: _Using only defaults_
@@ -754,6 +755,396 @@ module namespace 'br:bicep/modules/service-bus.namespace:1.0.0' = {
+### Example 5: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/service-bus/namespace/tests/e2e/waf-aligned/dependencies.bicep b/modules/service-bus/namespace/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..07a2e7878c
--- /dev/null
+++ b/modules/service-bus/namespace/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,63 @@
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ }
+ }
+ ]
+ }
+}
+
+resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
+ name: 'privatelink.servicebus.windows.net'
+ location: 'global'
+
+ resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = {
+ name: '${virtualNetwork.name}-vnetlink'
+ location: 'global'
+ properties: {
+ virtualNetwork: {
+ id: virtualNetwork.id
+ }
+ registrationEnabled: false
+ }
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Virtual Network Subnet.')
+output subnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
+
+@description('The resource ID of the created Private DNS Zone.')
+output privateDNSZoneResourceId string = privateDNSZone.id
diff --git a/modules/service-bus/namespace/tests/e2e/waf-aligned/main.test.bicep b/modules/service-bus/namespace/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..2d7aac3873
--- /dev/null
+++ b/modules/service-bus/namespace/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,226 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-servicebus.namespaces-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'sbnwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ skuName: 'Premium'
+ skuCapacity: 2
+ premiumMessagingPartitions: 1
+ zoneRedundant: true
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ networkRuleSets: {
+ defaultAction: 'Deny'
+ trustedServiceAccessEnabled: true
+ virtualNetworkRules: [
+ {
+ ignoreMissingVnetServiceEndpoint: true
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ }
+ ]
+ ipRules: [
+ {
+ ipMask: '10.0.1.0/32'
+ action: 'Allow'
+ }
+ {
+ ipMask: '10.0.2.0/32'
+ action: 'Allow'
+ }
+ ]
+ }
+ authorizationRules: [
+ {
+ name: 'RootManageSharedAccessKey'
+ rights: [
+ 'Listen'
+ 'Manage'
+ 'Send'
+ ]
+ }
+ {
+ name: 'AnotherKey'
+ rights: [
+ 'Listen'
+ 'Send'
+ ]
+ }
+ ]
+ queues: [
+ {
+ name: '${namePrefix}${serviceShort}q001'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ authorizationRules: [
+ {
+ name: 'RootManageSharedAccessKey'
+ rights: [
+ 'Listen'
+ 'Manage'
+ 'Send'
+ ]
+ }
+ {
+ name: 'AnotherKey'
+ rights: [
+ 'Listen'
+ 'Send'
+ ]
+ }
+ ]
+ autoDeleteOnIdle: 'PT5M'
+ maxMessageSizeInKilobytes: 2048
+ }
+ ]
+ topics: [
+ {
+ name: '${namePrefix}${serviceShort}t001'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ authorizationRules: [
+ {
+ name: 'RootManageSharedAccessKey'
+ rights: [
+ 'Listen'
+ 'Manage'
+ 'Send'
+ ]
+ }
+ {
+ name: 'AnotherKey'
+ rights: [
+ 'Listen'
+ 'Send'
+ ]
+ }
+ ]
+ }
+ ]
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ privateEndpoints: [
+ {
+ service: 'namespace'
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.privateDNSZoneResourceId
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ managedIdentities: {
+ systemAssigned: true
+ userAssignedResourcesIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ disableLocalAuth: true
+ publicNetworkAccess: 'Enabled'
+ minimumTlsVersion: '1.2'
+ }
+}
diff --git a/modules/service-fabric/cluster/README.md b/modules/service-fabric/cluster/README.md
index ff6dbe1f65..e24432c80e 100644
--- a/modules/service-fabric/cluster/README.md
+++ b/modules/service-fabric/cluster/README.md
@@ -31,6 +31,7 @@ The following section provides usage examples for the module, which were used to
- [Cert](#example-1-cert)
- [Using only defaults](#example-2-using-only-defaults)
- [Using large parameter set](#example-3-using-large-parameter-set)
+- [WAF-aligned](#example-4-waf-aligned)
### Example 1: _Cert_
@@ -649,6 +650,420 @@ module cluster 'br:bicep/modules/service-fabric.cluster:1.0.0' = {
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/service-fabric/cluster/tests/e2e/waf-aligned/dependencies.bicep b/modules/service-fabric/cluster/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..3cf8c25ddd
--- /dev/null
+++ b/modules/service-fabric/cluster/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,31 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the storage account to create.')
+param storageAccountName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = {
+ name: storageAccountName
+ location: location
+ kind: 'StorageV2'
+ sku: {
+ name: 'Standard_LRS'
+ }
+ properties: {
+ allowBlobPublicAccess: false
+ }
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The name of the created Storage Account.')
+output storageAccountName string = storageAccount.name
diff --git a/modules/service-fabric/cluster/tests/e2e/waf-aligned/main.test.bicep b/modules/service-fabric/cluster/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..6b1ad668cc
--- /dev/null
+++ b/modules/service-fabric/cluster/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,225 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-servicefabric.clusters-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'sfcwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ storageAccountName: 'dep${namePrefix}azsa${serviceShort}01'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ resourceType: 'Service Fabric'
+ clusterName: '${namePrefix}${serviceShort}001'
+ }
+ addOnFeatures: [
+ 'RepairManager'
+ 'DnsService'
+ 'BackupRestoreService'
+ 'ResourceMonitorService'
+ ]
+ maxUnusedVersionsToKeep: 2
+ azureActiveDirectory: {
+ clientApplication: nestedDependencies.outputs.managedIdentityPrincipalId
+ clusterApplication: 'cf33fea8-b30f-424f-ab73-c48d99e0b222'
+ tenantId: tenant().tenantId
+ }
+ certificateCommonNames: {
+ commonNames: [
+ {
+ certificateCommonName: 'certcommon'
+ certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130'
+ }
+ ]
+ x509StoreName: ''
+ }
+ clientCertificateCommonNames: [
+ {
+ certificateCommonName: 'clientcommoncert1'
+ certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130'
+ isAdmin: false
+ }
+ {
+ certificateCommonName: 'clientcommoncert2'
+ certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131'
+ isAdmin: false
+ }
+ ]
+ clientCertificateThumbprints: [
+ {
+ certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130'
+ isAdmin: false
+ }
+ {
+ certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131'
+ isAdmin: false
+ }
+ ]
+ diagnosticsStorageAccountConfig: {
+ blobEndpoint: 'https://${nestedDependencies.outputs.storageAccountName}.blob.${environment().suffixes.storage}/'
+ protectedAccountKeyName: 'StorageAccountKey1'
+ queueEndpoint: 'https://${nestedDependencies.outputs.storageAccountName}.queue.${environment().suffixes.storage}/'
+ storageAccountName: nestedDependencies.outputs.storageAccountName
+ tableEndpoint: 'https://${nestedDependencies.outputs.storageAccountName}.table.${environment().suffixes.storage}/'
+ }
+ fabricSettings: [
+ {
+ name: 'Security'
+ parameters: [
+ {
+ name: 'ClusterProtectionLevel'
+ value: 'EncryptAndSign'
+ }
+ ]
+ }
+ {
+ name: 'UpgradeService'
+ parameters: [
+ {
+ name: 'AppPollIntervalInSeconds'
+ value: '60'
+ }
+ ]
+ }
+ ]
+ managementEndpoint: 'https://${namePrefix}${serviceShort}001.westeurope.cloudapp.azure.com:19080'
+ reliabilityLevel: 'Silver'
+ nodeTypes: [
+ {
+ applicationPorts: {
+ endPort: 30000
+ startPort: 20000
+ }
+ clientConnectionEndpointPort: 19000
+ durabilityLevel: 'Silver'
+ ephemeralPorts: {
+ endPort: 65534
+ startPort: 49152
+ }
+ httpGatewayEndpointPort: 19080
+ isPrimary: true
+ name: 'Node01'
+
+ isStateless: false
+ multipleAvailabilityZones: false
+
+ placementProperties: {}
+ reverseProxyEndpointPort: ''
+ vmInstanceCount: 5
+ }
+ {
+ applicationPorts: {
+ endPort: 30000
+ startPort: 20000
+ }
+ clientConnectionEndpointPort: 19000
+ durabilityLevel: 'Bronze'
+ ephemeralPorts: {
+ endPort: 64000
+ startPort: 49000
+ httpGatewayEndpointPort: 19007
+ isPrimary: true
+ name: 'Node02'
+ vmInstanceCount: 5
+ }
+ }
+ ]
+ notifications: [
+ {
+ isEnabled: true
+ notificationCategory: 'WaveProgress'
+ notificationLevel: 'Critical'
+ notificationTargets: [
+ {
+ notificationChannel: 'EmailUser'
+ receivers: [
+ 'SomeReceiver'
+ ]
+ }
+ ]
+ }
+ ]
+ upgradeDescription: {
+ forceRestart: false
+ upgradeReplicaSetCheckTimeout: '1.00:00:00'
+ healthCheckWaitDuration: '00:00:30'
+ healthCheckStableDuration: '00:01:00'
+ healthCheckRetryTimeout: '00:45:00'
+ upgradeTimeout: '02:00:00'
+ upgradeDomainTimeout: '02:00:00'
+ healthPolicy: {
+ maxPercentUnhealthyNodes: 0
+ maxPercentUnhealthyApplications: 0
+ }
+ deltaHealthPolicy: {
+ maxPercentDeltaUnhealthyNodes: 0
+ maxPercentUpgradeDomainDeltaUnhealthyNodes: 0
+ maxPercentDeltaUnhealthyApplications: 0
+ }
+
+ }
+ vmImage: 'Linux'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ applicationTypes: [
+ {
+ name: 'WordCount' // not idempotent
+ }
+ ]
+ }
+}
diff --git a/modules/signal-r-service/signal-r/README.md b/modules/signal-r-service/signal-r/README.md
index 8a20ce6637..0650ea90d4 100644
--- a/modules/signal-r-service/signal-r/README.md
+++ b/modules/signal-r-service/signal-r/README.md
@@ -30,6 +30,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -271,6 +272,198 @@ module signalR 'br:bicep/modules/signal-r-service.signal-r:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/signal-r-service/signal-r/tests/e2e/waf-aligned/dependencies.bicep b/modules/signal-r-service/signal-r/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..3f02e7b5ad
--- /dev/null
+++ b/modules/signal-r-service/signal-r/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,62 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ privateEndpointNetworkPolicies: 'Disabled'
+ privateLinkServiceNetworkPolicies: 'Enabled'
+ }
+ }
+ ]
+ }
+}
+
+resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
+ name: 'privatelink.service.signalr.net'
+ location: 'global'
+
+ resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = {
+ name: '${virtualNetwork.name}-vnetlink'
+ location: 'global'
+ properties: {
+ virtualNetwork: {
+ id: virtualNetwork.id
+ }
+ registrationEnabled: false
+ }
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Virtual Network Subnet.')
+output subnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The resource ID of the created Private DNS Zone.')
+output privateDNSZoneResourceId string = privateDNSZone.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/signal-r-service/signal-r/tests/e2e/waf-aligned/main.test.bicep b/modules/signal-r-service/signal-r/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..5c88da4283
--- /dev/null
+++ b/modules/signal-r-service/signal-r/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,117 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-signalrservice.signalr-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'srssrwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// =========== //
+// Deployments //
+// =========== //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-paramNested'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}-${serviceShort}-001'
+ capacity: 2
+ clientCertEnabled: false
+ disableAadAuth: false
+ disableLocalAuth: true
+ location: location
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ kind: 'SignalR'
+ networkAcls: {
+ defaultAction: 'Allow'
+ privateEndpoints: [
+ {
+ allow: []
+ deny: [
+ 'ServerConnection'
+ 'Trace'
+ ]
+ name: 'pe-${namePrefix}-${serviceShort}-001'
+
+ }
+ ]
+ publicNetwork: {
+ allow: []
+ deny: [
+ 'RESTAPI'
+ 'Trace'
+ ]
+ }
+ }
+ privateEndpoints: [
+ {
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.privateDNSZoneResourceId
+ ]
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ resourceLogConfigurationsToEnable: [
+ 'ConnectivityLogs'
+ ]
+ roleAssignments: [
+ {
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ roleDefinitionIdOrName: 'Reader'
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ sku: 'Standard_S1'
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/signal-r-service/web-pub-sub/README.md b/modules/signal-r-service/web-pub-sub/README.md
index de04a9437c..80d94432be 100644
--- a/modules/signal-r-service/web-pub-sub/README.md
+++ b/modules/signal-r-service/web-pub-sub/README.md
@@ -31,6 +31,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
- [Pe](#example-3-pe)
+- [WAF-aligned](#example-4-waf-aligned)
### Example 1: _Using only defaults_
@@ -367,6 +368,204 @@ module webPubSub 'br:bicep/modules/signal-r-service.web-pub-sub:1.0.0' = {
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/signal-r-service/web-pub-sub/tests/e2e/waf-aligned/dependencies.bicep b/modules/signal-r-service/web-pub-sub/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..53f60ba74f
--- /dev/null
+++ b/modules/signal-r-service/web-pub-sub/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,62 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ privateEndpointNetworkPolicies: 'Disabled'
+ privateLinkServiceNetworkPolicies: 'Enabled'
+ }
+ }
+ ]
+ }
+}
+
+resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
+ name: 'privatelink.webpubsub.azure.com'
+ location: 'global'
+
+ resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = {
+ name: '${virtualNetwork.name}-vnetlink'
+ location: 'global'
+ properties: {
+ virtualNetwork: {
+ id: virtualNetwork.id
+ }
+ registrationEnabled: false
+ }
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Virtual Network Subnet.')
+output subnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The resource ID of the created Private DNS Zone.')
+output privateDNSZoneResourceId string = privateDNSZone.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/signal-r-service/web-pub-sub/tests/e2e/waf-aligned/main.test.bicep b/modules/signal-r-service/web-pub-sub/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..2391c085b0
--- /dev/null
+++ b/modules/signal-r-service/web-pub-sub/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,119 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-signalrservice.webpubsub-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'srswpswaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}-${serviceShort}-001'
+ capacity: 2
+ clientCertEnabled: false
+ disableAadAuth: false
+ disableLocalAuth: true
+ location: location
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ networkAcls: {
+ defaultAction: 'Allow'
+ privateEndpoints: [
+ {
+ allow: []
+ deny: [
+ 'ServerConnection'
+ 'Trace'
+ ]
+ name: 'pe-${namePrefix}-${serviceShort}-001'
+ }
+ ]
+ publicNetwork: {
+ allow: []
+ deny: [
+ 'RESTAPI'
+ 'Trace'
+ ]
+ }
+ }
+ privateEndpoints: [
+ {
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.privateDNSZoneResourceId
+ ]
+ service: 'webpubsub'
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ resourceLogConfigurationsToEnable: [
+ 'ConnectivityLogs'
+ ]
+ roleAssignments: [
+ {
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ roleDefinitionIdOrName: 'Reader'
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ sku: 'Standard_S1'
+ managedIdentities: {
+ systemAssigned: true
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/sql/managed-instance/README.md b/modules/sql/managed-instance/README.md
index 14c4696753..c16e126709 100644
--- a/modules/sql/managed-instance/README.md
+++ b/modules/sql/managed-instance/README.md
@@ -39,6 +39,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
- [Vulnassm](#example-3-vulnassm)
+- [WAF-aligned](#example-4-waf-aligned)
### Example 1: _Using only defaults_
@@ -505,6 +506,298 @@ module managedInstance 'br:bicep/modules/sql.managed-instance:1.0.0' = {
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/sql/managed-instance/tests/e2e/waf-aligned/dependencies.bicep b/modules/sql/managed-instance/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..c4e9dfd575
--- /dev/null
+++ b/modules/sql/managed-instance/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,350 @@
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Network Security Group to create.')
+param networkSecurityGroupName string
+
+@description('Required. The name of the Route Table to create.')
+param routeTableName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the Key Vault to create.')
+param keyVaultName string
+
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+var addressPrefix = '10.0.0.0/16'
+var addressPrefixString = replace(replace(addressPrefix, '.', '-'), '/', '-')
+
+resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = {
+ name: networkSecurityGroupName
+ location: location
+ properties: {
+ securityRules: [
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-sqlmgmt-in-${addressPrefixString}-v10'
+ properties: {
+ description: 'Allow MI provisioning Control Plane Deployment and Authentication Service'
+ protocol: 'Tcp'
+ sourcePortRange: '*'
+ sourceAddressPrefix: 'SqlManagement'
+ destinationAddressPrefix: addressPrefix
+ access: 'Allow'
+ priority: 100
+ direction: 'Inbound'
+ destinationPortRanges: [
+ '9000'
+ '9003'
+ '1438'
+ '1440'
+ '1452'
+ ]
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corpsaw-in-${addressPrefixString}-v10'
+ properties: {
+ description: 'Allow MI Supportability'
+ protocol: 'Tcp'
+ sourcePortRange: '*'
+ sourceAddressPrefix: 'CorpNetSaw'
+ destinationAddressPrefix: addressPrefix
+ access: 'Allow'
+ priority: 101
+ direction: 'Inbound'
+ destinationPortRanges: [
+ '9000'
+ '9003'
+ '1440'
+ ]
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corppublic-in-${addressPrefixString}-v10'
+ properties: {
+ description: 'Allow MI Supportability through Corpnet ranges'
+ protocol: 'Tcp'
+ sourcePortRange: '*'
+ sourceAddressPrefix: 'CorpNetPublic'
+ destinationAddressPrefix: addressPrefix
+ access: 'Allow'
+ priority: 102
+ direction: 'Inbound'
+ destinationPortRanges: [
+ '9000'
+ '9003'
+ ]
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-healthprobe-in-${addressPrefixString}-v10'
+ properties: {
+ description: 'Allow Azure Load Balancer inbound traffic'
+ protocol: '*'
+ sourcePortRange: '*'
+ destinationPortRange: '*'
+ sourceAddressPrefix: 'AzureLoadBalancer'
+ destinationAddressPrefix: addressPrefix
+ access: 'Allow'
+ priority: 103
+ direction: 'Inbound'
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-in-${addressPrefixString}-v10'
+ properties: {
+ description: 'Allow MI internal inbound traffic'
+ protocol: '*'
+ sourcePortRange: '*'
+ destinationPortRange: '*'
+ sourceAddressPrefix: addressPrefix
+ destinationAddressPrefix: addressPrefix
+ access: 'Allow'
+ priority: 104
+ direction: 'Inbound'
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-services-out-${addressPrefixString}-v10'
+ properties: {
+ description: 'Allow MI services outbound traffic over https'
+ protocol: 'Tcp'
+ sourcePortRange: '*'
+ sourceAddressPrefix: addressPrefix
+ destinationAddressPrefix: 'AzureCloud'
+ access: 'Allow'
+ priority: 100
+ direction: 'Outbound'
+ destinationPortRanges: [
+ '443'
+ '12000'
+ ]
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-out-${addressPrefixString}-v10'
+ properties: {
+ description: 'Allow MI internal outbound traffic'
+ protocol: '*'
+ sourcePortRange: '*'
+ destinationPortRange: '*'
+ sourceAddressPrefix: addressPrefix
+ destinationAddressPrefix: addressPrefix
+ access: 'Allow'
+ priority: 101
+ direction: 'Outbound'
+ }
+ }
+ ]
+ }
+}
+
+resource routeTable 'Microsoft.Network/routeTables@2023-04-01' = {
+ name: routeTableName
+ location: location
+ properties: {
+ disableBgpRoutePropagation: false
+ routes: [
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_subnet-${addressPrefixString}-to-vnetlocal'
+ properties: {
+ addressPrefix: addressPrefix
+ nextHopType: 'VnetLocal'
+ hasBgpOverride: false
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage'
+ properties: {
+ addressPrefix: 'Storage'
+ nextHopType: 'Internet'
+ hasBgpOverride: false
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-SqlManagement'
+ properties: {
+ addressPrefix: 'SqlManagement'
+ nextHopType: 'Internet'
+ hasBgpOverride: false
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureMonitor'
+ properties: {
+ addressPrefix: 'AzureMonitor'
+ nextHopType: 'Internet'
+ hasBgpOverride: false
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetSaw'
+ properties: {
+ addressPrefix: 'CorpNetSaw'
+ nextHopType: 'Internet'
+ hasBgpOverride: false
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetPublic'
+ properties: {
+ addressPrefix: 'CorpNetPublic'
+ nextHopType: 'Internet'
+ hasBgpOverride: false
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureActiveDirectory'
+ properties: {
+ addressPrefix: 'AzureActiveDirectory'
+ nextHopType: 'Internet'
+ hasBgpOverride: false
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureCloud.westeurope'
+ properties: {
+ addressPrefix: 'AzureCloud.westeurope'
+ nextHopType: 'Internet'
+ hasBgpOverride: false
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureCloud.northeurope'
+ properties: {
+ addressPrefix: 'AzureCloud.northeurope'
+ nextHopType: 'Internet'
+ hasBgpOverride: false
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.westeurope'
+ properties: {
+ addressPrefix: 'Storage.westeurope'
+ nextHopType: 'Internet'
+ hasBgpOverride: false
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.northeurope'
+ properties: {
+ addressPrefix: 'Storage.northeurope'
+ nextHopType: 'Internet'
+ hasBgpOverride: false
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-EventHub.westeurope'
+ properties: {
+ addressPrefix: 'EventHub.westeurope'
+ nextHopType: 'Internet'
+ hasBgpOverride: false
+ }
+ }
+ {
+ name: 'Microsoft.Sql-managedInstances_UseOnly_mi-EventHub.northeurope'
+ properties: {
+ addressPrefix: 'EventHub.northeurope'
+ nextHopType: 'Internet'
+ hasBgpOverride: false
+ }
+ }
+ ]
+ }
+}
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'ManagedInstance'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ routeTable: {
+ id: routeTable.id
+ }
+ networkSecurityGroup: {
+ id: networkSecurityGroup.id
+ }
+ delegations: [
+ {
+ name: 'managedInstanceDelegation'
+ properties: {
+ serviceName: 'Microsoft.Sql/managedInstances'
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
+ name: keyVaultName
+ location: location
+ properties: {
+ sku: {
+ family: 'A'
+ name: 'standard'
+ }
+ tenantId: tenant().tenantId
+ enablePurgeProtection: true
+ softDeleteRetentionInDays: 7
+ enabledForTemplateDeployment: true
+ enabledForDiskEncryption: true
+ enabledForDeployment: true
+ enableRbacAuthorization: true
+ accessPolicies: []
+ }
+
+ resource key 'keys@2022-07-01' = {
+ name: 'keyEncryptionKey'
+ properties: {
+ kty: 'RSA'
+ }
+ }
+}
+
+resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
+ name: guid('msi-${keyVault::key.id}-${location}-${managedIdentity.id}-Key-Reader-RoleAssignment')
+ scope: keyVault::key
+ properties: {
+ principalId: managedIdentity.properties.principalId
+ roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6') // Key Vault Crypto Service Encryption User
+ principalType: 'ServicePrincipal'
+ }
+}
+
+@description('The resource ID of the created Virtual Network Subnet.')
+output subnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
+
+@description('The URL of the created Key Vault Encryption Key.')
+output keyVaultEncryptionKeyUrl string = keyVault::key.properties.keyUriWithVersion
+
+@description('The name of the created Key Vault Encryption Key.')
+output keyVaultKeyName string = keyVault::key.name
+
+@description('The name of the created Key Vault.')
+output keyVaultName string = keyVault.name
diff --git a/modules/sql/managed-instance/tests/e2e/waf-aligned/main.test.bicep b/modules/sql/managed-instance/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..c5846900f8
--- /dev/null
+++ b/modules/sql/managed-instance/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,181 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-sql.managedinstances-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'sqlmiwaf'
+
+@description('Generated. Used as a basis for unique resource names.')
+param baseTime string = utcNow('u')
+
+@description('Optional. The password to leverage for the login.')
+@secure()
+param password string = newGuid()
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total)
+ keyVaultName: 'dep${namePrefix}kv${serviceShort}${substring(uniqueString(baseTime), 0, 3)}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}'
+ routeTableName: 'dep-${namePrefix}-rt-${serviceShort}'
+ location: location
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}azsa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}-${serviceShort}'
+ administratorLogin: 'adminUserName'
+ administratorLoginPassword: password
+ subnetId: nestedDependencies.outputs.subnetResourceId
+ collation: 'SQL_Latin1_General_CP1_CI_AS'
+ databases: [
+ {
+ backupLongTermRetentionPolicies: {
+ name: 'default'
+ }
+ backupShortTermRetentionPolicies: {
+ name: 'default'
+ }
+ name: '${namePrefix}-${serviceShort}-db-001'
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ }
+ ]
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ dnsZonePartner: ''
+ encryptionProtectorObj: {
+ serverKeyName: '${nestedDependencies.outputs.keyVaultName}_${nestedDependencies.outputs.keyVaultKeyName}_${last(split(nestedDependencies.outputs.keyVaultEncryptionKeyUrl, '/'))}'
+ serverKeyType: 'AzureKeyVault'
+ }
+ hardwareFamily: 'Gen5'
+ keys: [
+ {
+ name: '${nestedDependencies.outputs.keyVaultName}_${nestedDependencies.outputs.keyVaultKeyName}_${last(split(nestedDependencies.outputs.keyVaultEncryptionKeyUrl, '/'))}'
+ serverKeyType: 'AzureKeyVault'
+ uri: nestedDependencies.outputs.keyVaultEncryptionKeyUrl
+ }
+ ]
+ licenseType: 'LicenseIncluded'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ primaryUserAssignedIdentityId: nestedDependencies.outputs.managedIdentityResourceId
+ proxyOverride: 'Proxy'
+ publicDataEndpointEnabled: false
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ securityAlertPoliciesObj: {
+ emailAccountAdmins: true
+ name: 'default'
+ state: 'Enabled'
+ }
+ servicePrincipal: 'SystemAssigned'
+ skuName: 'GP_Gen5'
+ skuTier: 'GeneralPurpose'
+ storageSizeInGB: 32
+ managedIdentities: {
+ systemAssigned: true
+ userAssignedResourcesIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ timezoneId: 'UTC'
+ vCores: 4
+ vulnerabilityAssessmentsObj: {
+ emailSubscriptionAdmins: true
+ name: 'default'
+ recurringScansEmails: [
+ 'test1@contoso.com'
+ 'test2@contoso.com'
+ ]
+ recurringScansIsEnabled: true
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ }
+}
diff --git a/modules/sql/server/README.md b/modules/sql/server/README.md
index 329f0f3f82..95b1c24ad9 100644
--- a/modules/sql/server/README.md
+++ b/modules/sql/server/README.md
@@ -45,6 +45,7 @@ The following section provides usage examples for the module, which were used to
- [Pe](#example-3-pe)
- [Secondary](#example-4-secondary)
- [Vulnassm](#example-5-vulnassm)
+- [WAF-aligned](#example-6-waf-aligned)
### Example 1: _Admin_
@@ -734,6 +735,324 @@ module server 'br:bicep/modules/sql.server:1.0.0' = {
+### Example 6: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/sql/server/tests/e2e/waf-aligned/dependencies.bicep b/modules/sql/server/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..5f68856202
--- /dev/null
+++ b/modules/sql/server/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,111 @@
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Key Vault to create.')
+param keyVaultName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: map(range(0, 2), i => {
+ name: 'subnet-${i}'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 24, i)
+ }
+ })
+ }
+}
+
+resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
+ name: 'privatelink${environment().suffixes.sqlServerHostname}'
+ location: 'global'
+
+ resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = {
+ name: '${virtualNetwork.name}-vnetlink'
+ location: 'global'
+ properties: {
+ virtualNetwork: {
+ id: virtualNetwork.id
+ }
+ registrationEnabled: false
+ }
+ }
+}
+
+resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
+ name: keyVaultName
+ location: location
+ properties: {
+ sku: {
+ family: 'A'
+ name: 'standard'
+ }
+ tenantId: tenant().tenantId
+ enablePurgeProtection: null
+ enabledForTemplateDeployment: true
+ enabledForDiskEncryption: true
+ enabledForDeployment: true
+ enableRbacAuthorization: true
+ accessPolicies: []
+ }
+
+ resource key 'keys@2022-07-01' = {
+ name: 'keyEncryptionKey'
+ properties: {
+ kty: 'RSA'
+ }
+ }
+}
+
+resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
+ name: guid('msi-${keyVault::key.id}-${location}-${managedIdentity.id}-Key-Vault-Crypto-Service-Encryption-User-RoleAssignment')
+ scope: keyVault::key
+ properties: {
+ principalId: managedIdentity.properties.principalId
+ roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6') // Key Vault Crypto Service Encryption User
+ principalType: 'ServicePrincipal'
+ }
+}
+
+@description('The principal ID of the created managed identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created managed identity.')
+output managedIdentityResourceId string = managedIdentity.id
+
+@description('The resource ID of the created virtual network subnet for a Private Endpoint.')
+output privateEndpointSubnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The resource ID of the created virtual network subnet for a Service Endpoint.')
+output serviceEndpointSubnetResourceId string = virtualNetwork.properties.subnets[1].id
+
+@description('The resource ID of the created Private DNS Zone.')
+output privateDNSZoneResourceId string = privateDNSZone.id
+
+@description('The URL of the created Key Vault Encryption Key.')
+output keyVaultEncryptionKeyUrl string = keyVault::key.properties.keyUriWithVersion
+
+@description('The name of the created Key Vault Encryption Key.')
+output keyVaultKeyName string = keyVault::key.name
+
+@description('The name of the created Key Vault.')
+output keyVaultName string = keyVault.name
diff --git a/modules/sql/server/tests/e2e/waf-aligned/main.test.bicep b/modules/sql/server/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..c9e7ee69cf
--- /dev/null
+++ b/modules/sql/server/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,197 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-sql.servers-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'sqlswaf'
+
+@description('Optional. The password to leverage for the login.')
+@secure()
+param password string = newGuid()
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ location: location
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}azsa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}-${serviceShort}'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ primaryUserAssignedIdentityId: nestedDependencies.outputs.managedIdentityResourceId
+ administratorLogin: 'adminUserName'
+ administratorLoginPassword: password
+ location: location
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ vulnerabilityAssessmentsObj: {
+ name: 'default'
+ emailSubscriptionAdmins: true
+ recurringScansIsEnabled: true
+ recurringScansEmails: [
+ 'test1@contoso.com'
+ 'test2@contoso.com'
+ ]
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ }
+ elasticPools: [
+ {
+ name: '${namePrefix}-${serviceShort}-ep-001'
+ skuName: 'GP_Gen5'
+ skuTier: 'GeneralPurpose'
+ skuCapacity: 10
+ // Pre-existing 'public' configuration
+ maintenanceConfigurationId: '${subscription().id}/providers/Microsoft.Maintenance/publicMaintenanceConfigurations/SQL_${location}_DB_1'
+ }
+ ]
+ databases: [
+ {
+ name: '${namePrefix}-${serviceShort}db-001'
+ collation: 'SQL_Latin1_General_CP1_CI_AS'
+ skuTier: 'GeneralPurpose'
+ skuName: 'ElasticPool'
+ capacity: 0
+ maxSizeBytes: 34359738368
+ licenseType: 'LicenseIncluded'
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ elasticPoolId: '${resourceGroup.id}/providers/Microsoft.Sql/servers/${namePrefix}-${serviceShort}/elasticPools/${namePrefix}-${serviceShort}-ep-001'
+ encryptionProtectorObj: {
+ serverKeyType: 'AzureKeyVault'
+ serverKeyName: '${nestedDependencies.outputs.keyVaultName}_${nestedDependencies.outputs.keyVaultKeyName}_${last(split(nestedDependencies.outputs.keyVaultEncryptionKeyUrl, '/'))}'
+ }
+ backupShortTermRetentionPolicy: {
+ retentionDays: 14
+ }
+ backupLongTermRetentionPolicy: {
+ monthlyRetention: 'P6M'
+ }
+ }
+ ]
+ firewallRules: [
+ {
+ name: 'AllowAllWindowsAzureIps'
+ endIpAddress: '0.0.0.0'
+ startIpAddress: '0.0.0.0'
+ }
+ ]
+ securityAlertPolicies: [
+ {
+ name: 'Default'
+ state: 'Enabled'
+ emailAccountAdmins: true
+ }
+ ]
+ keys: [
+ {
+ name: '${nestedDependencies.outputs.keyVaultName}_${nestedDependencies.outputs.keyVaultKeyName}_${last(split(nestedDependencies.outputs.keyVaultEncryptionKeyUrl, '/'))}'
+ serverKeyType: 'AzureKeyVault'
+ uri: nestedDependencies.outputs.keyVaultEncryptionKeyUrl
+ }
+ ]
+ managedIdentities: {
+ systemAssigned: true
+ userAssignedResourcesIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ privateEndpoints: [
+ {
+ subnetResourceId: nestedDependencies.outputs.privateEndpointSubnetResourceId
+ service: 'sqlServer'
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.privateDNSZoneResourceId
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ virtualNetworkRules: [
+ {
+ ignoreMissingVnetServiceEndpoint: true
+ name: 'newVnetRule1'
+ virtualNetworkSubnetId: nestedDependencies.outputs.serviceEndpointSubnetResourceId
+ }
+ ]
+ restrictOutboundNetworkAccess: 'Disabled'
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/storage/storage-account/README.md b/modules/storage/storage-account/README.md
index 137e38dee2..4add2e1cc2 100644
--- a/modules/storage/storage-account/README.md
+++ b/modules/storage/storage-account/README.md
@@ -46,6 +46,7 @@ The following section provides usage examples for the module, which were used to
- [Using large parameter set](#example-3-using-large-parameter-set)
- [Nfs](#example-4-nfs)
- [V1](#example-5-v1)
+- [WAF-aligned](#example-6-waf-aligned)
### Example 1: _Using only defaults_
@@ -1108,6 +1109,620 @@ module storageAccount 'br:bicep/modules/storage.storage-account:1.0.0' = {
+### Example 6: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/storage/storage-account/tests/e2e/waf-aligned/dependencies.bicep b/modules/storage/storage-account/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..b7cff8b3d2
--- /dev/null
+++ b/modules/storage/storage-account/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,68 @@
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ serviceEndpoints: [
+ {
+ service: 'Microsoft.Storage'
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
+
+resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
+ name: 'privatelink.blob.${environment().suffixes.storage}'
+ location: 'global'
+
+ resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = {
+ name: '${virtualNetwork.name}-vnetlink'
+ location: 'global'
+ properties: {
+ virtualNetwork: {
+ id: virtualNetwork.id
+ }
+ registrationEnabled: false
+ }
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Virtual Network Subnet.')
+output subnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
+
+@description('The resource ID of the created Private DNS Zone.')
+output privateDNSZoneResourceId string = privateDNSZone.id
diff --git a/modules/storage/storage-account/tests/e2e/waf-aligned/main.test.bicep b/modules/storage/storage-account/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..0c03921624
--- /dev/null
+++ b/modules/storage/storage-account/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,333 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-storage.storageaccounts-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'ssawaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ skuName: 'Standard_LRS'
+ allowBlobPublicAccess: false
+ requireInfrastructureEncryption: true
+ largeFileSharesState: 'Enabled'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ enableHierarchicalNamespace: true
+ enableSftp: true
+ enableNfsV3: true
+ privateEndpoints: [
+ {
+ service: 'blob'
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.privateDNSZoneResourceId
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ networkAcls: {
+ bypass: 'AzureServices'
+ defaultAction: 'Deny'
+ virtualNetworkRules: [
+ {
+ action: 'Allow'
+ id: nestedDependencies.outputs.subnetResourceId
+ }
+ ]
+ ipRules: [
+ {
+ action: 'Allow'
+ value: '1.1.1.1'
+ }
+ ]
+ }
+ localUsers: [
+ {
+ storageAccountName: '${namePrefix}${serviceShort}001'
+ name: 'testuser'
+ hasSharedKey: false
+ hasSshKey: true
+ hasSshPassword: false
+ homeDirectory: 'avdscripts'
+ permissionScopes: [
+ {
+ permissions: 'r'
+ service: 'blob'
+ resourceName: 'avdscripts'
+ }
+ ]
+ }
+ ]
+ blobServices: {
+ lastAccessTimeTrackingPolicyEnabled: true
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ containers: [
+ {
+ name: 'avdscripts'
+ enableNfsV3AllSquash: true
+ enableNfsV3RootSquash: true
+ publicAccess: 'None'
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ }
+ {
+ name: 'archivecontainer'
+ publicAccess: 'None'
+ metadata: {
+ testKey: 'testValue'
+ }
+ enableWORM: true
+ WORMRetention: 666
+ allowProtectedAppendWrites: false
+ }
+ ]
+ automaticSnapshotPolicyEnabled: true
+ containerDeleteRetentionPolicyEnabled: true
+ containerDeleteRetentionPolicyDays: 10
+ deleteRetentionPolicyEnabled: true
+ deleteRetentionPolicyDays: 9
+ }
+ fileServices: {
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ shares: [
+ {
+ name: 'avdprofiles'
+ accessTier: 'Hot'
+ shareQuota: 5120
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ }
+ {
+ name: 'avdprofiles2'
+ shareQuota: 102400
+ }
+ ]
+ }
+ tableServices: {
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ tables: [
+ 'table1'
+ 'table2'
+ ]
+ }
+ queueServices: {
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ queues: [
+ {
+ name: 'queue1'
+ metadata: {
+ key1: 'value1'
+ key2: 'value2'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ }
+ {
+ name: 'queue2'
+ metadata: {}
+ }
+ ]
+ }
+ sasExpirationPeriod: '180.00:00:00'
+ managedIdentities: {
+ systemAssigned: true
+ userAssignedResourcesIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ managementPolicyRules: [
+ {
+ enabled: true
+ name: 'FirstRule'
+ type: 'Lifecycle'
+ definition: {
+ actions: {
+ baseBlob: {
+ delete: {
+ daysAfterModificationGreaterThan: 30
+ }
+ tierToCool: {
+ daysAfterLastAccessTimeGreaterThan: 5
+ }
+ }
+ }
+ filters: {
+ blobIndexMatch: [
+ {
+ name: 'BlobIndex'
+ op: '=='
+ value: '1'
+ }
+ ]
+ blobTypes: [
+ 'blockBlob'
+ ]
+ prefixMatch: [
+ 'sample-container/log'
+ ]
+ }
+ }
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/synapse/private-link-hub/README.md b/modules/synapse/private-link-hub/README.md
index 6e5a8a801c..c023d34f2e 100644
--- a/modules/synapse/private-link-hub/README.md
+++ b/modules/synapse/private-link-hub/README.md
@@ -30,6 +30,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -205,6 +206,132 @@ module privateLinkHub 'br:bicep/modules/synapse.private-link-hub:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/synapse/private-link-hub/tests/e2e/waf-aligned/dependencies.bicep b/modules/synapse/private-link-hub/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..d7ca02fccb
--- /dev/null
+++ b/modules/synapse/private-link-hub/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,74 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Network Security Group to create.')
+param networkSecurityGroupName string
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = {
+ name: networkSecurityGroupName
+ location: location
+ properties: {}
+}
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ networkSecurityGroup: {
+ id: networkSecurityGroup.id
+ }
+ privateEndpointNetworkPolicies: 'Disabled'
+ privateLinkServiceNetworkPolicies: 'Enabled'
+ }
+ }
+ ]
+ }
+}
+
+resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
+ name: 'privatelink.azuresynapse.net'
+ location: 'global'
+
+ resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = {
+ name: '${virtualNetwork.name}-vnetlink'
+ location: 'global'
+ properties: {
+ virtualNetwork: {
+ id: virtualNetwork.id
+ }
+ registrationEnabled: false
+ }
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Virtual Network Subnet.')
+output subnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The resource ID of the created Private DNS Zone.')
+output privateDNSZoneResourceId string = privateDNSZone.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/synapse/private-link-hub/tests/e2e/waf-aligned/main.test.bicep b/modules/synapse/private-link-hub/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..c5b50dbbd7
--- /dev/null
+++ b/modules/synapse/private-link-hub/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,93 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-synapse.privatelinkhubs-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'splhwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}'
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ privateEndpoints: [
+ {
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.privateDNSZoneResourceId
+ ]
+ service: 'Web'
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ {
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c'
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/synapse/workspace/README.md b/modules/synapse/workspace/README.md
index d00edcb815..879cf28301 100644
--- a/modules/synapse/workspace/README.md
+++ b/modules/synapse/workspace/README.md
@@ -37,6 +37,7 @@ The following section provides usage examples for the module, which were used to
- [Encrwuai](#example-3-encrwuai)
- [Managedvnet](#example-4-managedvnet)
- [Using large parameter set](#example-5-using-large-parameter-set)
+- [WAF-aligned](#example-6-waf-aligned)
### Example 1: _Using only defaults_
@@ -507,6 +508,178 @@ module workspace 'br:bicep/modules/synapse.workspace:1.0.0' = {
+### Example 6: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/synapse/workspace/tests/e2e/waf-aligned/dependencies.bicep b/modules/synapse/workspace/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..52da267176
--- /dev/null
+++ b/modules/synapse/workspace/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,92 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Storage Account to create.')
+param storageAccountName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ }
+ }
+ ]
+ }
+}
+
+resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
+ name: 'privatelink.sql.azuresynapse.net'
+ location: 'global'
+
+ resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = {
+ name: '${virtualNetworkName}-vnetlink'
+ location: 'global'
+ properties: {
+ virtualNetwork: {
+ id: virtualNetwork.id
+ }
+ registrationEnabled: false
+ }
+ }
+}
+
+resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {
+ name: storageAccountName
+ location: location
+ sku: {
+ name: 'Standard_LRS'
+ }
+ kind: 'StorageV2'
+ properties: {
+ isHnsEnabled: true
+ }
+
+ resource blobService 'blobServices@2022-09-01' = {
+ name: 'default'
+
+ resource container 'containers@2022-09-01' = {
+ name: 'synapsews'
+ }
+ }
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
+
+@description('The resource ID of the created Virtual Network Subnet.')
+output subnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The resource ID of the created Private DNS Zone.')
+output privateDNSZoneResourceId string = privateDNSZone.id
+
+@description('The resource ID of the created Storage Account.')
+output storageAccountResourceId string = storageAccount.id
+
+@description('The name of the created container.')
+output storageContainerName string = storageAccount::blobService::container.name
diff --git a/modules/synapse/workspace/tests/e2e/waf-aligned/main.test.bicep b/modules/synapse/workspace/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..cd02520ced
--- /dev/null
+++ b/modules/synapse/workspace/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,124 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-synapse.workspaces-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'swwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ storageAccountName: 'dep${namePrefix}sa${serviceShort}01'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ location: location
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ defaultDataLakeStorageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId
+ defaultDataLakeStorageFilesystem: nestedDependencies.outputs.storageContainerName
+ sqlAdministratorLogin: 'synwsadmin'
+ initialWorkspaceAdminObjectID: nestedDependencies.outputs.managedIdentityPrincipalId
+ userAssignedIdentities: {
+ '${nestedDependencies.outputs.managedIdentityResourceId}': {}
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ privateEndpoints: [
+ {
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ service: 'SQL'
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.privateDNSZoneResourceId
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ managedVirtualNetwork: true
+ integrationRuntimes: [
+ {
+ type: 'SelfHosted'
+ name: 'shir01'
+ }
+ ]
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ logCategoriesAndGroups: [
+ {
+ category: 'SynapseRbacOperations'
+ }
+ {
+ category: 'SynapseLinkEvent'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ enableDefaultTelemetry: enableDefaultTelemetry
+ }
+}
diff --git a/modules/virtual-machine-images/image-template/README.md b/modules/virtual-machine-images/image-template/README.md
index d5d30e9144..d58507d074 100644
--- a/modules/virtual-machine-images/image-template/README.md
+++ b/modules/virtual-machine-images/image-template/README.md
@@ -29,6 +29,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -292,6 +293,178 @@ module imageTemplate 'br:bicep/modules/virtual-machine-images.image-template:1.0
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/virtual-machine-images/image-template/tests/e2e/waf-aligned/dependencies.bicep b/modules/virtual-machine-images/image-template/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..ec4e08c2d4
--- /dev/null
+++ b/modules/virtual-machine-images/image-template/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,99 @@
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Shared Image Gallery to create.')
+param galleryName string
+
+@description('Required. The name of the Image Definition to create in the Shared Image Gallery.')
+param sigImageDefinitionName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Optional. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+var addressPrefix = '10.0.0.0/16'
+
+resource gallery 'Microsoft.Compute/galleries@2022-03-03' = {
+ name: galleryName
+ location: location
+ properties: {}
+}
+
+resource galleryImageDefinition 'Microsoft.Compute/galleries/images@2022-03-03' = {
+ name: sigImageDefinitionName
+ location: location
+ parent: gallery
+ properties: {
+ architecture: 'x64'
+ hyperVGeneration: 'V2'
+ identifier: {
+ offer: 'Windows-11'
+ publisher: 'MicrosoftWindowsDesktop'
+ sku: 'Win11-AVD-g2'
+ }
+ osState: 'Generalized'
+ osType: 'Windows'
+ recommended: {
+ memory: {
+ max: 16
+ min: 4
+ }
+ vCPUs: {
+ max: 8
+ min: 2
+ }
+ }
+ }
+}
+
+resource msi_contibutorRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
+ name: guid(resourceGroup().id, 'Contributor', '[[namePrefix]]')
+ properties: {
+ roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') // Contributor
+ principalId: managedIdentity.properties.principalId
+ principalType: 'ServicePrincipal'
+ }
+}
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ privateLinkServiceNetworkPolicies: 'Disabled'
+ }
+ }
+ ]
+ }
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The name of the created Managed Identity.')
+output managedIdentityName string = managedIdentity.name
+
+@description('The resource ID of the created Image Definition.')
+output sigImageDefinitionId string = galleryImageDefinition.id
+
+@description('The subnet resource id of the defaultSubnet of the created Virtual Network.')
+output subnetId string = '${virtualNetwork.id}/subnets/defaultSubnet'
diff --git a/modules/virtual-machine-images/image-template/tests/e2e/waf-aligned/main.test.bicep b/modules/virtual-machine-images/image-template/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..7e2e523fee
--- /dev/null
+++ b/modules/virtual-machine-images/image-template/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,119 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-virtualmachineimages.imagetemplates-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'vmiitwaf'
+
+@description('Optional. The version of the Azure Compute Gallery Image Definition to be added.')
+param sigImageVersion string = utcNow('yyyy.MM.dd')
+
+@description('Optional. The staging resource group name in the same location and subscription as the image template. Must not exist.')
+param stagingResourceGroupName string = 'ms.virtualmachineimages.imagetemplates-${serviceShort}-staging-rg'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ sigImageDefinitionName: 'dep-${namePrefix}-imgd-${serviceShort}'
+ galleryName: 'dep${namePrefix}sig${serviceShort}'
+ virtualNetworkName: 'dep${namePrefix}-vnet-${serviceShort}'
+ }
+}
+
+// required for the Azure Image Builder service to assign the list of User Assigned Identities to the Build VM.
+resource msi_managedIdentityOperatorRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
+ name: guid(subscription().id, 'ManagedIdentityContributor', '${namePrefix}')
+ properties: {
+ roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830') // Managed Identity Operator
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ customizationSteps: [
+ {
+ restartTimeout: '10m'
+ type: 'WindowsRestart'
+ }
+ ]
+ imageSource: {
+ offer: 'Windows-11'
+ publisher: 'MicrosoftWindowsDesktop'
+ sku: 'win11-22h2-avd'
+ type: 'PlatformImage'
+ version: 'latest'
+ }
+ buildTimeoutInMinutes: 60
+ imageReplicationRegions: []
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ managedImageName: '${namePrefix}-mi-${serviceShort}-001'
+ osDiskSizeGB: 127
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ sigImageDefinitionId: nestedDependencies.outputs.sigImageDefinitionId
+ sigImageVersion: sigImageVersion
+ subnetId: nestedDependencies.outputs.subnetId
+ stagingResourceGroup: '${subscription().id}/resourcegroups/${stagingResourceGroupName}'
+ unManagedImageName: '${namePrefix}-umi-${serviceShort}-001'
+ userAssignedIdentities: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ userMsiName: nestedDependencies.outputs.managedIdentityName
+ userMsiResourceGroup: resourceGroupName
+ vmSize: 'Standard_D2s_v3'
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/web/connection/README.md b/modules/web/connection/README.md
index bdb9491881..682936b91b 100644
--- a/modules/web/connection/README.md
+++ b/modules/web/connection/README.md
@@ -27,6 +27,7 @@ The following section provides usage examples for the module, which were used to
>**Note**: To reference the module, please use the following syntax `br:bicep/modules/web.connection:1.0.0`.
- [Using large parameter set](#example-1-using-large-parameter-set)
+- [WAF-aligned](#example-2-waf-aligned)
### Example 1: _Using large parameter set_
@@ -126,6 +127,104 @@ module connection 'br:bicep/modules/web.connection:1.0.0' = {
+### Example 2: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/web/connection/tests/e2e/waf-aligned/dependencies.bicep b/modules/web/connection/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/web/connection/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/web/connection/tests/e2e/waf-aligned/main.test.bicep b/modules/web/connection/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..acc6afbcd9
--- /dev/null
+++ b/modules/web/connection/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,77 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-web.connections-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'wcwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ displayName: 'azuremonitorlogs'
+ name: 'azuremonitor'
+ api: {
+ id: '${subscription().id}/providers/Microsoft.Web/locations/westeurope/managedApis/azuremonitorlogs'
+
+ }
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/web/serverfarm/README.md b/modules/web/serverfarm/README.md
index 4dc832d2b9..0f9579209f 100644
--- a/modules/web/serverfarm/README.md
+++ b/modules/web/serverfarm/README.md
@@ -28,6 +28,7 @@ The following section provides usage examples for the module, which were used to
>**Note**: To reference the module, please use the following syntax `br:bicep/modules/web.serverfarm:1.0.0`.
- [Using large parameter set](#example-1-using-large-parameter-set)
+- [WAF-aligned](#example-2-waf-aligned)
### Example 1: _Using large parameter set_
@@ -161,6 +162,138 @@ module serverfarm 'br:bicep/modules/web.serverfarm:1.0.0' = {
+### Example 2: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/web/serverfarm/tests/e2e/waf-aligned/dependencies.bicep b/modules/web/serverfarm/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/modules/web/serverfarm/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/modules/web/serverfarm/tests/e2e/waf-aligned/main.test.bicep b/modules/web/serverfarm/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..b6be6a4df6
--- /dev/null
+++ b/modules/web/serverfarm/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,107 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-web.serverfarms-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'wsfwaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../.shared/.templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ sku: {
+ capacity: '1'
+ family: 'S'
+ name: 'S1'
+ size: 'S1'
+ tier: 'Standard'
+ }
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
diff --git a/modules/web/static-site/README.md b/modules/web/static-site/README.md
index ebd2b09d90..98a80f18d6 100644
--- a/modules/web/static-site/README.md
+++ b/modules/web/static-site/README.md
@@ -33,6 +33,7 @@ The following section provides usage examples for the module, which were used to
- [Using only defaults](#example-1-using-only-defaults)
- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
### Example 1: _Using only defaults_
@@ -254,6 +255,178 @@ module staticSite 'br:bicep/modules/web.static-site:1.0.0' = {
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
## Parameters
diff --git a/modules/web/static-site/tests/e2e/waf-aligned/dependencies.bicep b/modules/web/static-site/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..7939cfd2d2
--- /dev/null
+++ b/modules/web/static-site/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,94 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the Function App to create.')
+param siteName string
+
+@description('Required. The name of the Server Farm to create.')
+param serverFarmName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ }
+ }
+ ]
+ }
+}
+
+resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
+ name: 'privatelink.azurestaticapps.net'
+ location: 'global'
+
+ resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = {
+ name: '${virtualNetwork.name}-vnetlink'
+ location: 'global'
+ properties: {
+ virtualNetwork: {
+ id: virtualNetwork.id
+ }
+ registrationEnabled: false
+ }
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource serverFarm 'Microsoft.Web/serverfarms@2022-03-01' = {
+ name: serverFarmName
+ location: location
+ sku: {
+ name: 'S1'
+ tier: 'Standard'
+ size: 'S1'
+ family: 'S'
+ capacity: 1
+ }
+ properties: {}
+}
+
+resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
+ name: siteName
+ location: location
+ kind: 'functionapp'
+ properties: {
+ serverFarmId: serverFarm.id
+ }
+}
+
+@description('The resource ID of the created Virtual Network Subnet.')
+output subnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
+
+@description('The resource ID of the created Private DNS zone.')
+output privateDNSZoneResourceId string = privateDNSZone.id
+
+@description('The resource ID of the created Function App.')
+output siteResourceId string = functionApp.id
diff --git a/modules/web/static-site/tests/e2e/waf-aligned/main.test.bicep b/modules/web/static-site/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..0b1be9250e
--- /dev/null
+++ b/modules/web/static-site/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,109 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-web.staticsites-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'wsswaf'
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableDefaultTelemetry bool = true
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '[[namePrefix]]'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ siteName: 'dep-${namePrefix}-fa-${serviceShort}'
+ serverFarmName: 'dep-${namePrefix}-sf-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+module testDeployment '../../../main.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
+ params: {
+ enableDefaultTelemetry: enableDefaultTelemetry
+ name: '${namePrefix}${serviceShort}001'
+ allowConfigFileUpdates: true
+ enterpriseGradeCdnStatus: 'Disabled'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ privateEndpoints: [
+ {
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.privateDNSZoneResourceId
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Reader'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ sku: 'Standard'
+ stagingEnvironmentPolicy: 'Enabled'
+ managedIdentities: {
+ systemAssigned: true
+ userAssignedResourcesIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ appSettings: {
+ foo: 'bar'
+ setting: 1
+ }
+ functionAppSettings: {
+ foo: 'bar'
+ setting: 1
+ }
+ linkedBackend: {
+ resourceId: nestedDependencies.outputs.siteResourceId
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
via Bicep module
+
+```bicep
+module workspace 'br:bicep/modules/machine-learning-services.workspace:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-mlswwaf'
+ params: {
+ // Required parameters
+ associatedApplicationInsightsResourceId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "associatedApplicationInsightsResourceId": {
+ "value": "via Bicep module
+
+```bicep
+module maintenanceConfiguration 'br:bicep/modules/maintenance.maintenance-configuration:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-mmcwaf'
+ params: {
+ // Required parameters
+ name: 'mmcwaf001'
+ // Non-required parameters
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "mmcwaf001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module userAssignedIdentity 'br:bicep/modules/managed-identity.user-assigned-identity:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-miuaiwaf'
+ params: {
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module registrationDefinition 'br:bicep/modules/managed-services.registration-definition:1.0.0' = {
+ name: '${uniqueString(deployment().name)}-test-msrdwaf'
+ params: {
+ // Required parameters
+ authorizations: [
+ {
+ principalId: '<< SET YOUR PRINCIPAL ID 1 HERE >>'
+ principalIdDisplayName: 'ResourceModules-Reader'
+ roleDefinitionId: 'acdd72a7-3385-48ef-bd42-f606fba81ae7'
+ }
+ {
+ principalId: '<< SET YOUR PRINCIPAL ID 2 HERE >>'
+ principalIdDisplayName: 'ResourceModules-Contributor'
+ roleDefinitionId: 'b24988ac-6180-42a0-ab88-20f7382dd24c'
+ }
+ {
+ principalId: '<< SET YOUR PRINCIPAL ID 3 HERE >>'
+ principalIdDisplayName: 'ResourceModules-LHManagement'
+ roleDefinitionId: '91c1777a-f3dc-4fae-b103-61d183457e46'
+ }
+ ]
+ managedByTenantId: '<< SET YOUR TENANT ID HERE >>'
+ name: 'Component Validation - msrdwaf Subscription assignment'
+ registrationDescription: 'Managed by Lighthouse'
+ // Non-required parameters
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "authorizations": {
+ "value": [
+ {
+ "principalId": "<< SET YOUR PRINCIPAL ID 1 HERE >>",
+ "principalIdDisplayName": "ResourceModules-Reader",
+ "roleDefinitionId": "acdd72a7-3385-48ef-bd42-f606fba81ae7"
+ },
+ {
+ "principalId": "<< SET YOUR PRINCIPAL ID 2 HERE >>",
+ "principalIdDisplayName": "ResourceModules-Contributor",
+ "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c"
+ },
+ {
+ "principalId": "<< SET YOUR PRINCIPAL ID 3 HERE >>",
+ "principalIdDisplayName": "ResourceModules-LHManagement",
+ "roleDefinitionId": "91c1777a-f3dc-4fae-b103-61d183457e46"
+ }
+ ]
+ },
+ "managedByTenantId": {
+ "value": "<< SET YOUR TENANT ID HERE >>"
+ },
+ "name": {
+ "value": "Component Validation - msrdwaf Subscription assignment"
+ },
+ "registrationDescription": {
+ "value": "Managed by Lighthouse"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module managementGroup 'br:bicep/modules/management.management-group:1.0.0' = {
+ name: '${uniqueString(deployment().name)}-test-mmgwaf'
+ params: {
+ // Required parameters
+ name: 'mmgwaf001'
+ // Non-required parameters
+ displayName: 'Test MG'
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "mmgwaf001"
+ },
+ // Non-required parameters
+ "displayName": {
+ "value": "Test MG"
+ },
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module applicationGatewayWebApplicationFirewallPolicy 'br:bicep/modules/network.application-gateway-web-application-firewall-policy:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nagwafpwaf'
+ params: {
+ // Required parameters
+ name: 'nagwafpwaf001'
+ // Non-required parameters
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nagwafpwaf001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module applicationGateway 'br:bicep/modules/network.application-gateway:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nagwaf'
+ params: {
+ // Required parameters
+ name: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "via Bicep module
+
+```bicep
+module applicationSecurityGroup 'br:bicep/modules/network.application-security-group:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nasgwaf'
+ params: {
+ // Required parameters
+ name: 'nasgwaf001'
+ // Non-required parameters
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nasgwaf001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module azureFirewall 'br:bicep/modules/network.azure-firewall:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nafwaf'
+ params: {
+ // Required parameters
+ name: 'nafwaf001'
+ // Non-required parameters
+ applicationRuleCollections: [
+ {
+ name: 'allow-app-rules'
+ properties: {
+ action: {
+ type: 'allow'
+ }
+ priority: 100
+ rules: [
+ {
+ fqdnTags: [
+ 'AppServiceEnvironment'
+ 'WindowsUpdate'
+ ]
+ name: 'allow-ase-tags'
+ protocols: [
+ {
+ port: '80'
+ protocolType: 'HTTP'
+ }
+ {
+ port: '443'
+ protocolType: 'HTTPS'
+ }
+ ]
+ sourceAddresses: [
+ '*'
+ ]
+ }
+ {
+ name: 'allow-ase-management'
+ protocols: [
+ {
+ port: '80'
+ protocolType: 'HTTP'
+ }
+ {
+ port: '443'
+ protocolType: 'HTTPS'
+ }
+ ]
+ sourceAddresses: [
+ '*'
+ ]
+ targetFqdns: [
+ 'bing.com'
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nafwaf001"
+ },
+ // Non-required parameters
+ "applicationRuleCollections": {
+ "value": [
+ {
+ "name": "allow-app-rules",
+ "properties": {
+ "action": {
+ "type": "allow"
+ },
+ "priority": 100,
+ "rules": [
+ {
+ "fqdnTags": [
+ "AppServiceEnvironment",
+ "WindowsUpdate"
+ ],
+ "name": "allow-ase-tags",
+ "protocols": [
+ {
+ "port": "80",
+ "protocolType": "HTTP"
+ },
+ {
+ "port": "443",
+ "protocolType": "HTTPS"
+ }
+ ],
+ "sourceAddresses": [
+ "*"
+ ]
+ },
+ {
+ "name": "allow-ase-management",
+ "protocols": [
+ {
+ "port": "80",
+ "protocolType": "HTTP"
+ },
+ {
+ "port": "443",
+ "protocolType": "HTTPS"
+ }
+ ],
+ "sourceAddresses": [
+ "*"
+ ],
+ "targetFqdns": [
+ "bing.com"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "via Bicep module
+
+```bicep
+module bastionHost 'br:bicep/modules/network.bastion-host:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nbhwaf'
+ params: {
+ // Required parameters
+ name: 'nbhwaf001'
+ vNetId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nbhwaf001"
+ },
+ "vNetId": {
+ "value": "via Bicep module
+
+```bicep
+module ddosProtectionPlan 'br:bicep/modules/network.ddos-protection-plan:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-ndppwaf'
+ params: {
+ // Required parameters
+ name: 'ndppwaf001'
+ // Non-required parameters
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "ndppwaf001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module dnsForwardingRuleset 'br:bicep/modules/network.dns-forwarding-ruleset:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-ndfrswaf'
+ params: {
+ // Required parameters
+ dnsResolverOutboundEndpointResourceIds: [
+ 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "dnsResolverOutboundEndpointResourceIds": {
+ "value": [
+ "via Bicep module
+
+```bicep
+module dnsResolver 'br:bicep/modules/network.dns-resolver:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-ndrwaf'
+ params: {
+ // Required parameters
+ name: 'ndrwaf001'
+ virtualNetworkId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "ndrwaf001"
+ },
+ "virtualNetworkId": {
+ "value": "via Bicep module
+
+```bicep
+module dnsZone 'br:bicep/modules/network.dns-zone:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-ndzwaf'
+ params: {
+ // Required parameters
+ name: 'ndzwaf001.com'
+ // Non-required parameters
+ a: [
+ {
+ aRecords: [
+ {
+ ipv4Address: '10.240.4.4'
+ }
+ ]
+ name: 'A_10.240.4.4'
+ roleAssignments: [
+ {
+ principalId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "ndzwaf001.com"
+ },
+ // Non-required parameters
+ "a": {
+ "value": [
+ {
+ "aRecords": [
+ {
+ "ipv4Address": "10.240.4.4"
+ }
+ ],
+ "name": "A_10.240.4.4",
+ "roleAssignments": [
+ {
+ "principalId": "via Bicep module
+
+```bicep
+module expressRouteCircuit 'br:bicep/modules/network.express-route-circuit:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nercwaf'
+ params: {
+ // Required parameters
+ bandwidthInMbps: 50
+ name: 'nercwaf001'
+ peeringLocation: 'Amsterdam'
+ serviceProviderName: 'Equinix'
+ // Non-required parameters
+ allowClassicOperations: true
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "bandwidthInMbps": {
+ "value": 50
+ },
+ "name": {
+ "value": "nercwaf001"
+ },
+ "peeringLocation": {
+ "value": "Amsterdam"
+ },
+ "serviceProviderName": {
+ "value": "Equinix"
+ },
+ // Non-required parameters
+ "allowClassicOperations": {
+ "value": true
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "via Bicep module
+
+```bicep
+module expressRouteGateway 'br:bicep/modules/network.express-route-gateway:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nergwaf'
+ params: {
+ // Required parameters
+ name: 'nergwaf001'
+ virtualHubId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nergwaf001"
+ },
+ "virtualHubId": {
+ "value": "via Bicep module
+
+```bicep
+module firewallPolicy 'br:bicep/modules/network.firewall-policy:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nfpwaf'
+ params: {
+ // Required parameters
+ name: 'nfpwaf001'
+ // Non-required parameters
+ allowSqlRedirect: true
+ autoLearnPrivateRanges: 'Enabled'
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nfpwaf001"
+ },
+ // Non-required parameters
+ "allowSqlRedirect": {
+ "value": true
+ },
+ "autoLearnPrivateRanges": {
+ "value": "Enabled"
+ },
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module frontDoorWebApplicationFirewallPolicy 'br:bicep/modules/network.front-door-web-application-firewall-policy:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nagwafpwaf'
+ params: {
+ // Required parameters
+ name: 'nagwafpwaf001'
+ // Non-required parameters
+ customRules: {
+ rules: [
+ {
+ action: 'Block'
+ enabledState: 'Enabled'
+ matchConditions: [
+ {
+ matchValue: [
+ 'CH'
+ ]
+ matchVariable: 'RemoteAddr'
+ negateCondition: false
+ operator: 'GeoMatch'
+ selector: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nagwafpwaf001"
+ },
+ // Non-required parameters
+ "customRules": {
+ "value": {
+ "rules": [
+ {
+ "action": "Block",
+ "enabledState": "Enabled",
+ "matchConditions": [
+ {
+ "matchValue": [
+ "CH"
+ ],
+ "matchVariable": "RemoteAddr",
+ "negateCondition": false,
+ "operator": "GeoMatch",
+ "selector": "via Bicep module
+
+```bicep
+module frontDoor 'br:bicep/modules/network.front-door:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nfdwaf'
+ params: {
+ // Required parameters
+ backendPools: [
+ {
+ name: 'backendPool'
+ properties: {
+ backends: [
+ {
+ address: 'biceptest.local'
+ backendHostHeader: 'backendAddress'
+ enabledState: 'Enabled'
+ httpPort: 80
+ httpsPort: 443
+ priority: 1
+ privateLinkAlias: ''
+ privateLinkApprovalMessage: ''
+ privateLinkLocation: ''
+ privateLinkResourceId: ''
+ weight: 50
+ }
+ ]
+ HealthProbeSettings: {
+ id: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "backendPools": {
+ "value": [
+ {
+ "name": "backendPool",
+ "properties": {
+ "backends": [
+ {
+ "address": "biceptest.local",
+ "backendHostHeader": "backendAddress",
+ "enabledState": "Enabled",
+ "httpPort": 80,
+ "httpsPort": 443,
+ "priority": 1,
+ "privateLinkAlias": "",
+ "privateLinkApprovalMessage": "",
+ "privateLinkLocation": "",
+ "privateLinkResourceId": "",
+ "weight": 50
+ }
+ ],
+ "HealthProbeSettings": {
+ "id": "via Bicep module
+
+```bicep
+module ipGroup 'br:bicep/modules/network.ip-group:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nigwaf'
+ params: {
+ // Required parameters
+ name: 'nigwaf001'
+ // Non-required parameters
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nigwaf001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module loadBalancer 'br:bicep/modules/network.load-balancer:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nlbwaf'
+ params: {
+ // Required parameters
+ frontendIPConfigurations: [
+ {
+ name: 'publicIPConfig1'
+ publicIPAddressId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "frontendIPConfigurations": {
+ "value": [
+ {
+ "name": "publicIPConfig1",
+ "publicIPAddressId": "via Bicep module
+
+```bicep
+module localNetworkGateway 'br:bicep/modules/network.local-network-gateway:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nlngwaf'
+ params: {
+ // Required parameters
+ localAddressPrefixes: [
+ '192.168.1.0/24'
+ ]
+ localGatewayPublicIpAddress: '8.8.8.8'
+ name: 'nlngwaf001'
+ // Non-required parameters
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "localAddressPrefixes": {
+ "value": [
+ "192.168.1.0/24"
+ ]
+ },
+ "localGatewayPublicIpAddress": {
+ "value": "8.8.8.8"
+ },
+ "name": {
+ "value": "nlngwaf001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module natGateway 'br:bicep/modules/network.nat-gateway:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nngwaf'
+ params: {
+ // Required parameters
+ name: 'nngwaf001'
+ // Non-required parameters
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nngwaf001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module networkInterface 'br:bicep/modules/network.network-interface:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nniwaf'
+ params: {
+ // Required parameters
+ ipConfigurations: [
+ {
+ applicationSecurityGroups: [
+ {
+ id: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "ipConfigurations": {
+ "value": [
+ {
+ "applicationSecurityGroups": [
+ {
+ "id": "via Bicep module
+
+```bicep
+module networkManager 'br:bicep/modules/network.network-manager:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nnmwaf'
+ params: {
+ // Required parameters
+ name: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "via Bicep module
+
+```bicep
+module networkSecurityGroup 'br:bicep/modules/network.network-security-group:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nnsgwaf'
+ params: {
+ // Required parameters
+ name: 'nnsgwaf001'
+ // Non-required parameters
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nnsgwaf001"
+ },
+ // Non-required parameters
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "via Bicep module
+
+```bicep
+module networkWatcher 'br:bicep/modules/network.network-watcher:1.0.0' = {
+ name: '${uniqueString(deployment().name, testLocation)}-test-nnwwaf'
+ params: {
+ connectionMonitors: [
+ {
+ endpoints: [
+ {
+ name: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ "connectionMonitors": {
+ "value": [
+ {
+ "endpoints": [
+ {
+ "name": "via Bicep module
+
+```bicep
+module privateDnsZone 'br:bicep/modules/network.private-dns-zone:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-npdzwaf'
+ params: {
+ // Required parameters
+ name: 'npdzwaf001.com'
+ // Non-required parameters
+ a: [
+ {
+ aRecords: [
+ {
+ ipv4Address: '10.240.4.4'
+ }
+ ]
+ name: 'A_10.240.4.4'
+ roleAssignments: [
+ {
+ principalId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "npdzwaf001.com"
+ },
+ // Non-required parameters
+ "a": {
+ "value": [
+ {
+ "aRecords": [
+ {
+ "ipv4Address": "10.240.4.4"
+ }
+ ],
+ "name": "A_10.240.4.4",
+ "roleAssignments": [
+ {
+ "principalId": "via Bicep module
+
+```bicep
+module privateEndpoint 'br:bicep/modules/network.private-endpoint:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-npewaf'
+ params: {
+ // Required parameters
+ groupIds: [
+ 'vault'
+ ]
+ name: 'npewaf001'
+ serviceResourceId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "groupIds": {
+ "value": [
+ "vault"
+ ]
+ },
+ "name": {
+ "value": "npewaf001"
+ },
+ "serviceResourceId": {
+ "value": "via Bicep module
+
+```bicep
+module privateLinkService 'br:bicep/modules/network.private-link-service:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nplswaf'
+ params: {
+ // Required parameters
+ name: 'nplswaf001'
+ // Non-required parameters
+ autoApproval: {
+ subscriptions: [
+ '*'
+ ]
+ }
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nplswaf001"
+ },
+ // Non-required parameters
+ "autoApproval": {
+ "value": {
+ "subscriptions": [
+ "*"
+ ]
+ }
+ },
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module publicIpAddress 'br:bicep/modules/network.public-ip-address:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-npiawaf'
+ params: {
+ // Required parameters
+ name: 'npiawaf001'
+ // Non-required parameters
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "npiawaf001"
+ },
+ // Non-required parameters
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "via Bicep module
+
+```bicep
+module publicIpPrefix 'br:bicep/modules/network.public-ip-prefix:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-npipwaf'
+ params: {
+ // Required parameters
+ name: 'npipwaf001'
+ prefixLength: 28
+ // Non-required parameters
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "npipwaf001"
+ },
+ "prefixLength": {
+ "value": 28
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module routeTable 'br:bicep/modules/network.route-table:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nrtwaf'
+ params: {
+ // Required parameters
+ name: 'nrtwaf001'
+ // Non-required parameters
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nrtwaf001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module serviceEndpointPolicy 'br:bicep/modules/network.service-endpoint-policy:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nsnpwaf'
+ params: {
+ // Required parameters
+ name: 'nsnpwaf-001'
+ // Non-required parameters
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nsnpwaf-001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module trafficmanagerprofile 'br:bicep/modules/network.trafficmanagerprofile:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-ntmpwaf'
+ params: {
+ // Required parameters
+ name: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "via Bicep module
+
+```bicep
+module virtualHub 'br:bicep/modules/network.virtual-hub:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nvhwaf'
+ params: {
+ // Required parameters
+ addressPrefix: '10.1.0.0/16'
+ name: 'nvhwaf'
+ virtualWanId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "addressPrefix": {
+ "value": "10.1.0.0/16"
+ },
+ "name": {
+ "value": "nvhwaf"
+ },
+ "virtualWanId": {
+ "value": "via Bicep module
+
+```bicep
+module virtualNetwork 'br:bicep/modules/network.virtual-network:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nvnwaf'
+ params: {
+ // Required parameters
+ addressPrefixes: [
+ 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "addressPrefixes": {
+ "value": [
+ "via Bicep module
+
+```bicep
+module virtualWan 'br:bicep/modules/network.virtual-wan:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nvwwaf'
+ params: {
+ // Required parameters
+ name: 'nvwwaf001'
+ // Non-required parameters
+ allowBranchToBranchTraffic: true
+ allowVnetToVnetTraffic: true
+ disableVpnEncryption: true
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nvwwaf001"
+ },
+ // Non-required parameters
+ "allowBranchToBranchTraffic": {
+ "value": true
+ },
+ "allowVnetToVnetTraffic": {
+ "value": true
+ },
+ "disableVpnEncryption": {
+ "value": true
+ },
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module vpnGateway 'br:bicep/modules/network.vpn-gateway:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nvgwaf'
+ params: {
+ // Required parameters
+ name: 'nvgwaf001'
+ virtualHubResourceId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nvgwaf001"
+ },
+ "virtualHubResourceId": {
+ "value": "via Bicep module
+
+```bicep
+module vpnSite 'br:bicep/modules/network.vpn-site:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-nvswaf'
+ params: {
+ // Required parameters
+ name: 'nvswaf'
+ virtualWanId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nvswaf"
+ },
+ "virtualWanId": {
+ "value": "via Bicep module
+
+```bicep
+module workspace 'br:bicep/modules/operational-insights.workspace:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-oiwwaf'
+ params: {
+ // Required parameters
+ name: 'oiwwaf001'
+ // Non-required parameters
+ dailyQuotaGb: 10
+ dataSources: [
+ {
+ eventLogName: 'Application'
+ eventTypes: [
+ {
+ eventType: 'Error'
+ }
+ {
+ eventType: 'Warning'
+ }
+ {
+ eventType: 'Information'
+ }
+ ]
+ kind: 'WindowsEvent'
+ name: 'applicationEvent'
+ }
+ {
+ counterName: '% Processor Time'
+ instanceName: '*'
+ intervalSeconds: 60
+ kind: 'WindowsPerformanceCounter'
+ name: 'windowsPerfCounter1'
+ objectName: 'Processor'
+ }
+ {
+ kind: 'IISLogs'
+ name: 'sampleIISLog1'
+ state: 'OnPremiseEnabled'
+ }
+ {
+ kind: 'LinuxSyslog'
+ name: 'sampleSyslog1'
+ syslogName: 'kern'
+ syslogSeverities: [
+ {
+ severity: 'emerg'
+ }
+ {
+ severity: 'alert'
+ }
+ {
+ severity: 'crit'
+ }
+ {
+ severity: 'err'
+ }
+ {
+ severity: 'warning'
+ }
+ ]
+ }
+ {
+ kind: 'LinuxSyslogCollection'
+ name: 'sampleSyslogCollection1'
+ state: 'Enabled'
+ }
+ {
+ instanceName: '*'
+ intervalSeconds: 10
+ kind: 'LinuxPerformanceObject'
+ name: 'sampleLinuxPerf1'
+ objectName: 'Logical Disk'
+ syslogSeverities: [
+ {
+ counterName: '% Used Inodes'
+ }
+ {
+ counterName: 'Free Megabytes'
+ }
+ {
+ counterName: '% Used Space'
+ }
+ {
+ counterName: 'Disk Transfers/sec'
+ }
+ {
+ counterName: 'Disk Reads/sec'
+ }
+ {
+ counterName: 'Disk Writes/sec'
+ }
+ ]
+ }
+ {
+ kind: 'LinuxPerformanceCollection'
+ name: 'sampleLinuxPerfCollection1'
+ state: 'Enabled'
+ }
+ ]
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "oiwwaf001"
+ },
+ // Non-required parameters
+ "dailyQuotaGb": {
+ "value": 10
+ },
+ "dataSources": {
+ "value": [
+ {
+ "eventLogName": "Application",
+ "eventTypes": [
+ {
+ "eventType": "Error"
+ },
+ {
+ "eventType": "Warning"
+ },
+ {
+ "eventType": "Information"
+ }
+ ],
+ "kind": "WindowsEvent",
+ "name": "applicationEvent"
+ },
+ {
+ "counterName": "% Processor Time",
+ "instanceName": "*",
+ "intervalSeconds": 60,
+ "kind": "WindowsPerformanceCounter",
+ "name": "windowsPerfCounter1",
+ "objectName": "Processor"
+ },
+ {
+ "kind": "IISLogs",
+ "name": "sampleIISLog1",
+ "state": "OnPremiseEnabled"
+ },
+ {
+ "kind": "LinuxSyslog",
+ "name": "sampleSyslog1",
+ "syslogName": "kern",
+ "syslogSeverities": [
+ {
+ "severity": "emerg"
+ },
+ {
+ "severity": "alert"
+ },
+ {
+ "severity": "crit"
+ },
+ {
+ "severity": "err"
+ },
+ {
+ "severity": "warning"
+ }
+ ]
+ },
+ {
+ "kind": "LinuxSyslogCollection",
+ "name": "sampleSyslogCollection1",
+ "state": "Enabled"
+ },
+ {
+ "instanceName": "*",
+ "intervalSeconds": 10,
+ "kind": "LinuxPerformanceObject",
+ "name": "sampleLinuxPerf1",
+ "objectName": "Logical Disk",
+ "syslogSeverities": [
+ {
+ "counterName": "% Used Inodes"
+ },
+ {
+ "counterName": "Free Megabytes"
+ },
+ {
+ "counterName": "% Used Space"
+ },
+ {
+ "counterName": "Disk Transfers/sec"
+ },
+ {
+ "counterName": "Disk Reads/sec"
+ },
+ {
+ "counterName": "Disk Writes/sec"
+ }
+ ]
+ },
+ {
+ "kind": "LinuxPerformanceCollection",
+ "name": "sampleLinuxPerfCollection1",
+ "state": "Enabled"
+ }
+ ]
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "via Bicep module
+
+```bicep
+module capacity 'br:bicep/modules/power-bi-dedicated.capacity:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-pbdcapwaf'
+ params: {
+ // Required parameters
+ members: [
+ 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "members": {
+ "value": [
+ "via Bicep module
+
+```bicep
+module account 'br:bicep/modules/purview.account:1.0.0' = {
+ name: '${uniqueString(deployment().name)}-test-pvawaf'
+ params: {
+ // Required parameters
+ name: 'pvawaf001'
+ // Non-required parameters
+ accountPrivateEndpoints: [
+ {
+ privateDnsZoneResourceIds: [
+ 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "pvawaf001"
+ },
+ // Non-required parameters
+ "accountPrivateEndpoints": {
+ "value": [
+ {
+ "privateDnsZoneResourceIds": [
+ "via Bicep module
+
+```bicep
+module vault 'br:bicep/modules/recovery-services.vault:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-rsvwaf'
+ params: {
+ // Required parameters
+ name: 'rsvwaf001'
+ // Non-required parameters
+ backupConfig: {
+ enhancedSecurityState: 'Disabled'
+ softDeleteFeatureState: 'Disabled'
+ }
+ backupPolicies: [
+ {
+ name: 'VMpolicy'
+ properties: {
+ backupManagementType: 'AzureIaasVM'
+ instantRPDetails: {}
+ instantRpRetentionRangeInDays: 2
+ protectedItemsCount: 0
+ retentionPolicy: {
+ dailySchedule: {
+ retentionDuration: {
+ count: 180
+ durationType: 'Days'
+ }
+ retentionTimes: [
+ '2019-11-07T07:00:00Z'
+ ]
+ }
+ monthlySchedule: {
+ retentionDuration: {
+ count: 60
+ durationType: 'Months'
+ }
+ retentionScheduleFormatType: 'Weekly'
+ retentionScheduleWeekly: {
+ daysOfTheWeek: [
+ 'Sunday'
+ ]
+ weeksOfTheMonth: [
+ 'First'
+ ]
+ }
+ retentionTimes: [
+ '2019-11-07T07:00:00Z'
+ ]
+ }
+ retentionPolicyType: 'LongTermRetentionPolicy'
+ weeklySchedule: {
+ daysOfTheWeek: [
+ 'Sunday'
+ ]
+ retentionDuration: {
+ count: 12
+ durationType: 'Weeks'
+ }
+ retentionTimes: [
+ '2019-11-07T07:00:00Z'
+ ]
+ }
+ yearlySchedule: {
+ monthsOfYear: [
+ 'January'
+ ]
+ retentionDuration: {
+ count: 10
+ durationType: 'Years'
+ }
+ retentionScheduleFormatType: 'Weekly'
+ retentionScheduleWeekly: {
+ daysOfTheWeek: [
+ 'Sunday'
+ ]
+ weeksOfTheMonth: [
+ 'First'
+ ]
+ }
+ retentionTimes: [
+ '2019-11-07T07:00:00Z'
+ ]
+ }
+ }
+ schedulePolicy: {
+ schedulePolicyType: 'SimpleSchedulePolicy'
+ scheduleRunFrequency: 'Daily'
+ scheduleRunTimes: [
+ '2019-11-07T07:00:00Z'
+ ]
+ scheduleWeeklyFrequency: 0
+ }
+ timeZone: 'UTC'
+ }
+ }
+ {
+ name: 'sqlpolicy'
+ properties: {
+ backupManagementType: 'AzureWorkload'
+ protectedItemsCount: 0
+ settings: {
+ isCompression: true
+ issqlcompression: true
+ timeZone: 'UTC'
+ }
+ subProtectionPolicy: [
+ {
+ policyType: 'Full'
+ retentionPolicy: {
+ monthlySchedule: {
+ retentionDuration: {
+ count: 60
+ durationType: 'Months'
+ }
+ retentionScheduleFormatType: 'Weekly'
+ retentionScheduleWeekly: {
+ daysOfTheWeek: [
+ 'Sunday'
+ ]
+ weeksOfTheMonth: [
+ 'First'
+ ]
+ }
+ retentionTimes: [
+ '2019-11-07T22:00:00Z'
+ ]
+ }
+ retentionPolicyType: 'LongTermRetentionPolicy'
+ weeklySchedule: {
+ daysOfTheWeek: [
+ 'Sunday'
+ ]
+ retentionDuration: {
+ count: 104
+ durationType: 'Weeks'
+ }
+ retentionTimes: [
+ '2019-11-07T22:00:00Z'
+ ]
+ }
+ yearlySchedule: {
+ monthsOfYear: [
+ 'January'
+ ]
+ retentionDuration: {
+ count: 10
+ durationType: 'Years'
+ }
+ retentionScheduleFormatType: 'Weekly'
+ retentionScheduleWeekly: {
+ daysOfTheWeek: [
+ 'Sunday'
+ ]
+ weeksOfTheMonth: [
+ 'First'
+ ]
+ }
+ retentionTimes: [
+ '2019-11-07T22:00:00Z'
+ ]
+ }
+ }
+ schedulePolicy: {
+ schedulePolicyType: 'SimpleSchedulePolicy'
+ scheduleRunDays: [
+ 'Sunday'
+ ]
+ scheduleRunFrequency: 'Weekly'
+ scheduleRunTimes: [
+ '2019-11-07T22:00:00Z'
+ ]
+ scheduleWeeklyFrequency: 0
+ }
+ }
+ {
+ policyType: 'Differential'
+ retentionPolicy: {
+ retentionDuration: {
+ count: 30
+ durationType: 'Days'
+ }
+ retentionPolicyType: 'SimpleRetentionPolicy'
+ }
+ schedulePolicy: {
+ schedulePolicyType: 'SimpleSchedulePolicy'
+ scheduleRunDays: [
+ 'Monday'
+ ]
+ scheduleRunFrequency: 'Weekly'
+ scheduleRunTimes: [
+ '2017-03-07T02:00:00Z'
+ ]
+ scheduleWeeklyFrequency: 0
+ }
+ }
+ {
+ policyType: 'Log'
+ retentionPolicy: {
+ retentionDuration: {
+ count: 15
+ durationType: 'Days'
+ }
+ retentionPolicyType: 'SimpleRetentionPolicy'
+ }
+ schedulePolicy: {
+ scheduleFrequencyInMins: 120
+ schedulePolicyType: 'LogSchedulePolicy'
+ }
+ }
+ ]
+ workLoadType: 'SQLDataBase'
+ }
+ }
+ {
+ name: 'filesharepolicy'
+ properties: {
+ backupManagementType: 'AzureStorage'
+ protectedItemsCount: 0
+ retentionPolicy: {
+ dailySchedule: {
+ retentionDuration: {
+ count: 30
+ durationType: 'Days'
+ }
+ retentionTimes: [
+ '2019-11-07T04:30:00Z'
+ ]
+ }
+ retentionPolicyType: 'LongTermRetentionPolicy'
+ }
+ schedulePolicy: {
+ schedulePolicyType: 'SimpleSchedulePolicy'
+ scheduleRunFrequency: 'Daily'
+ scheduleRunTimes: [
+ '2019-11-07T04:30:00Z'
+ ]
+ scheduleWeeklyFrequency: 0
+ }
+ timeZone: 'UTC'
+ workloadType: 'AzureFileShare'
+ }
+ }
+ ]
+ backupStorageConfig: {
+ crossRegionRestoreFlag: true
+ storageModelType: 'GeoRedundant'
+ }
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "rsvwaf001"
+ },
+ // Non-required parameters
+ "backupConfig": {
+ "value": {
+ "enhancedSecurityState": "Disabled",
+ "softDeleteFeatureState": "Disabled"
+ }
+ },
+ "backupPolicies": {
+ "value": [
+ {
+ "name": "VMpolicy",
+ "properties": {
+ "backupManagementType": "AzureIaasVM",
+ "instantRPDetails": {},
+ "instantRpRetentionRangeInDays": 2,
+ "protectedItemsCount": 0,
+ "retentionPolicy": {
+ "dailySchedule": {
+ "retentionDuration": {
+ "count": 180,
+ "durationType": "Days"
+ },
+ "retentionTimes": [
+ "2019-11-07T07:00:00Z"
+ ]
+ },
+ "monthlySchedule": {
+ "retentionDuration": {
+ "count": 60,
+ "durationType": "Months"
+ },
+ "retentionScheduleFormatType": "Weekly",
+ "retentionScheduleWeekly": {
+ "daysOfTheWeek": [
+ "Sunday"
+ ],
+ "weeksOfTheMonth": [
+ "First"
+ ]
+ },
+ "retentionTimes": [
+ "2019-11-07T07:00:00Z"
+ ]
+ },
+ "retentionPolicyType": "LongTermRetentionPolicy",
+ "weeklySchedule": {
+ "daysOfTheWeek": [
+ "Sunday"
+ ],
+ "retentionDuration": {
+ "count": 12,
+ "durationType": "Weeks"
+ },
+ "retentionTimes": [
+ "2019-11-07T07:00:00Z"
+ ]
+ },
+ "yearlySchedule": {
+ "monthsOfYear": [
+ "January"
+ ],
+ "retentionDuration": {
+ "count": 10,
+ "durationType": "Years"
+ },
+ "retentionScheduleFormatType": "Weekly",
+ "retentionScheduleWeekly": {
+ "daysOfTheWeek": [
+ "Sunday"
+ ],
+ "weeksOfTheMonth": [
+ "First"
+ ]
+ },
+ "retentionTimes": [
+ "2019-11-07T07:00:00Z"
+ ]
+ }
+ },
+ "schedulePolicy": {
+ "schedulePolicyType": "SimpleSchedulePolicy",
+ "scheduleRunFrequency": "Daily",
+ "scheduleRunTimes": [
+ "2019-11-07T07:00:00Z"
+ ],
+ "scheduleWeeklyFrequency": 0
+ },
+ "timeZone": "UTC"
+ }
+ },
+ {
+ "name": "sqlpolicy",
+ "properties": {
+ "backupManagementType": "AzureWorkload",
+ "protectedItemsCount": 0,
+ "settings": {
+ "isCompression": true,
+ "issqlcompression": true,
+ "timeZone": "UTC"
+ },
+ "subProtectionPolicy": [
+ {
+ "policyType": "Full",
+ "retentionPolicy": {
+ "monthlySchedule": {
+ "retentionDuration": {
+ "count": 60,
+ "durationType": "Months"
+ },
+ "retentionScheduleFormatType": "Weekly",
+ "retentionScheduleWeekly": {
+ "daysOfTheWeek": [
+ "Sunday"
+ ],
+ "weeksOfTheMonth": [
+ "First"
+ ]
+ },
+ "retentionTimes": [
+ "2019-11-07T22:00:00Z"
+ ]
+ },
+ "retentionPolicyType": "LongTermRetentionPolicy",
+ "weeklySchedule": {
+ "daysOfTheWeek": [
+ "Sunday"
+ ],
+ "retentionDuration": {
+ "count": 104,
+ "durationType": "Weeks"
+ },
+ "retentionTimes": [
+ "2019-11-07T22:00:00Z"
+ ]
+ },
+ "yearlySchedule": {
+ "monthsOfYear": [
+ "January"
+ ],
+ "retentionDuration": {
+ "count": 10,
+ "durationType": "Years"
+ },
+ "retentionScheduleFormatType": "Weekly",
+ "retentionScheduleWeekly": {
+ "daysOfTheWeek": [
+ "Sunday"
+ ],
+ "weeksOfTheMonth": [
+ "First"
+ ]
+ },
+ "retentionTimes": [
+ "2019-11-07T22:00:00Z"
+ ]
+ }
+ },
+ "schedulePolicy": {
+ "schedulePolicyType": "SimpleSchedulePolicy",
+ "scheduleRunDays": [
+ "Sunday"
+ ],
+ "scheduleRunFrequency": "Weekly",
+ "scheduleRunTimes": [
+ "2019-11-07T22:00:00Z"
+ ],
+ "scheduleWeeklyFrequency": 0
+ }
+ },
+ {
+ "policyType": "Differential",
+ "retentionPolicy": {
+ "retentionDuration": {
+ "count": 30,
+ "durationType": "Days"
+ },
+ "retentionPolicyType": "SimpleRetentionPolicy"
+ },
+ "schedulePolicy": {
+ "schedulePolicyType": "SimpleSchedulePolicy",
+ "scheduleRunDays": [
+ "Monday"
+ ],
+ "scheduleRunFrequency": "Weekly",
+ "scheduleRunTimes": [
+ "2017-03-07T02:00:00Z"
+ ],
+ "scheduleWeeklyFrequency": 0
+ }
+ },
+ {
+ "policyType": "Log",
+ "retentionPolicy": {
+ "retentionDuration": {
+ "count": 15,
+ "durationType": "Days"
+ },
+ "retentionPolicyType": "SimpleRetentionPolicy"
+ },
+ "schedulePolicy": {
+ "scheduleFrequencyInMins": 120,
+ "schedulePolicyType": "LogSchedulePolicy"
+ }
+ }
+ ],
+ "workLoadType": "SQLDataBase"
+ }
+ },
+ {
+ "name": "filesharepolicy",
+ "properties": {
+ "backupManagementType": "AzureStorage",
+ "protectedItemsCount": 0,
+ "retentionPolicy": {
+ "dailySchedule": {
+ "retentionDuration": {
+ "count": 30,
+ "durationType": "Days"
+ },
+ "retentionTimes": [
+ "2019-11-07T04:30:00Z"
+ ]
+ },
+ "retentionPolicyType": "LongTermRetentionPolicy"
+ },
+ "schedulePolicy": {
+ "schedulePolicyType": "SimpleSchedulePolicy",
+ "scheduleRunFrequency": "Daily",
+ "scheduleRunTimes": [
+ "2019-11-07T04:30:00Z"
+ ],
+ "scheduleWeeklyFrequency": 0
+ },
+ "timeZone": "UTC",
+ "workloadType": "AzureFileShare"
+ }
+ }
+ ]
+ },
+ "backupStorageConfig": {
+ "value": {
+ "crossRegionRestoreFlag": true,
+ "storageModelType": "GeoRedundant"
+ }
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "via Bicep module
+
+```bicep
+module namespace 'br:bicep/modules/relay.namespace:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-rnwaf'
+ params: {
+ // Required parameters
+ name: 'rnwaf001'
+ // Non-required parameters
+ authorizationRules: [
+ {
+ name: 'RootManageSharedAccessKey'
+ rights: [
+ 'Listen'
+ 'Manage'
+ 'Send'
+ ]
+ }
+ {
+ name: 'AnotherKey'
+ rights: [
+ 'Listen'
+ 'Send'
+ ]
+ }
+ ]
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "rnwaf001"
+ },
+ // Non-required parameters
+ "authorizationRules": {
+ "value": [
+ {
+ "name": "RootManageSharedAccessKey",
+ "rights": [
+ "Listen",
+ "Manage",
+ "Send"
+ ]
+ },
+ {
+ "name": "AnotherKey",
+ "rights": [
+ "Listen",
+ "Send"
+ ]
+ }
+ ]
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "via Bicep module
+
+```bicep
+module query 'br:bicep/modules/resource-graph.query:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-rgqwaf'
+ params: {
+ // Required parameters
+ name: 'rgqwaf001'
+ query: 'resources | take 10'
+ // Non-required parameters
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "rgqwaf001"
+ },
+ "query": {
+ "value": "resources | take 10"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module resourceGroup 'br:bicep/modules/resources.resource-group:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-rrgwaf'
+ params: {
+ // Required parameters
+ name: 'rrgwaf001'
+ // Non-required parameters
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "rrgwaf001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module searchService 'br:bicep/modules/search.search-service:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-ssswaf'
+ params: {
+ // Required parameters
+ name: 'ssswaf001'
+ // Non-required parameters
+ authOptions: {
+ aadOrApiKey: {
+ aadAuthFailureMode: 'http401WithBearerChallenge'
+ }
+ }
+ cmkEnforcement: 'Enabled'
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "ssswaf001"
+ },
+ // Non-required parameters
+ "authOptions": {
+ "value": {
+ "aadOrApiKey": {
+ "aadAuthFailureMode": "http401WithBearerChallenge"
+ }
+ }
+ },
+ "cmkEnforcement": {
+ "value": "Enabled"
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "via Bicep module
+
+```bicep
+module azureSecurityCenter 'br:bicep/modules/security.azure-security-center:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-sascwaf'
+ params: {
+ // Required parameters
+ workspaceId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "workspaceId": {
+ "value": "via Bicep module
+
+```bicep
+module namespace 'br:bicep/modules/service-bus.namespace:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-sbnwaf'
+ params: {
+ // Required parameters
+ name: 'sbnwaf001'
+ // Non-required parameters
+ authorizationRules: [
+ {
+ name: 'RootManageSharedAccessKey'
+ rights: [
+ 'Listen'
+ 'Manage'
+ 'Send'
+ ]
+ }
+ {
+ name: 'AnotherKey'
+ rights: [
+ 'Listen'
+ 'Send'
+ ]
+ }
+ ]
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "sbnwaf001"
+ },
+ // Non-required parameters
+ "authorizationRules": {
+ "value": [
+ {
+ "name": "RootManageSharedAccessKey",
+ "rights": [
+ "Listen",
+ "Manage",
+ "Send"
+ ]
+ },
+ {
+ "name": "AnotherKey",
+ "rights": [
+ "Listen",
+ "Send"
+ ]
+ }
+ ]
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "via Bicep module
+
+```bicep
+module cluster 'br:bicep/modules/service-fabric.cluster:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-sfcwaf'
+ params: {
+ // Required parameters
+ managementEndpoint: 'https://sfcwaf001.westeurope.cloudapp.azure.com:19080'
+ name: 'sfcwaf001'
+ nodeTypes: [
+ {
+ applicationPorts: {
+ endPort: 30000
+ startPort: 20000
+ }
+ clientConnectionEndpointPort: 19000
+ durabilityLevel: 'Silver'
+ ephemeralPorts: {
+ endPort: 65534
+ startPort: 49152
+ }
+ httpGatewayEndpointPort: 19080
+ isPrimary: true
+ isStateless: false
+ multipleAvailabilityZones: false
+ name: 'Node01'
+ placementProperties: {}
+ reverseProxyEndpointPort: ''
+ vmInstanceCount: 5
+ }
+ {
+ applicationPorts: {
+ endPort: 30000
+ startPort: 20000
+ }
+ clientConnectionEndpointPort: 19000
+ durabilityLevel: 'Bronze'
+ ephemeralPorts: {
+ endPort: 64000
+ httpGatewayEndpointPort: 19007
+ isPrimary: true
+ name: 'Node02'
+ startPort: 49000
+ vmInstanceCount: 5
+ }
+ }
+ ]
+ reliabilityLevel: 'Silver'
+ // Non-required parameters
+ addOnFeatures: [
+ 'BackupRestoreService'
+ 'DnsService'
+ 'RepairManager'
+ 'ResourceMonitorService'
+ ]
+ applicationTypes: [
+ {
+ name: 'WordCount'
+ }
+ ]
+ azureActiveDirectory: {
+ clientApplication: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "managementEndpoint": {
+ "value": "https://sfcwaf001.westeurope.cloudapp.azure.com:19080"
+ },
+ "name": {
+ "value": "sfcwaf001"
+ },
+ "nodeTypes": {
+ "value": [
+ {
+ "applicationPorts": {
+ "endPort": 30000,
+ "startPort": 20000
+ },
+ "clientConnectionEndpointPort": 19000,
+ "durabilityLevel": "Silver",
+ "ephemeralPorts": {
+ "endPort": 65534,
+ "startPort": 49152
+ },
+ "httpGatewayEndpointPort": 19080,
+ "isPrimary": true,
+ "isStateless": false,
+ "multipleAvailabilityZones": false,
+ "name": "Node01",
+ "placementProperties": {},
+ "reverseProxyEndpointPort": "",
+ "vmInstanceCount": 5
+ },
+ {
+ "applicationPorts": {
+ "endPort": 30000,
+ "startPort": 20000
+ },
+ "clientConnectionEndpointPort": 19000,
+ "durabilityLevel": "Bronze",
+ "ephemeralPorts": {
+ "endPort": 64000,
+ "httpGatewayEndpointPort": 19007,
+ "isPrimary": true,
+ "name": "Node02",
+ "startPort": 49000,
+ "vmInstanceCount": 5
+ }
+ }
+ ]
+ },
+ "reliabilityLevel": {
+ "value": "Silver"
+ },
+ // Non-required parameters
+ "addOnFeatures": {
+ "value": [
+ "BackupRestoreService",
+ "DnsService",
+ "RepairManager",
+ "ResourceMonitorService"
+ ]
+ },
+ "applicationTypes": {
+ "value": [
+ {
+ "name": "WordCount"
+ }
+ ]
+ },
+ "azureActiveDirectory": {
+ "value": {
+ "clientApplication": "via Bicep module
+
+```bicep
+module signalR 'br:bicep/modules/signal-r-service.signal-r:1.0.0' = {
+ name: '${uniqueString(deployment().name)}-test-srssrwaf'
+ params: {
+ // Required parameters
+ name: 'srssrwaf-001'
+ // Non-required parameters
+ capacity: 2
+ clientCertEnabled: false
+ disableAadAuth: false
+ disableLocalAuth: true
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "srssrwaf-001"
+ },
+ // Non-required parameters
+ "capacity": {
+ "value": 2
+ },
+ "clientCertEnabled": {
+ "value": false
+ },
+ "disableAadAuth": {
+ "value": false
+ },
+ "disableLocalAuth": {
+ "value": true
+ },
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module webPubSub 'br:bicep/modules/signal-r-service.web-pub-sub:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-srswpswaf'
+ params: {
+ // Required parameters
+ name: 'srswpswaf-001'
+ // Non-required parameters
+ capacity: 2
+ clientCertEnabled: false
+ disableAadAuth: false
+ disableLocalAuth: true
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "srswpswaf-001"
+ },
+ // Non-required parameters
+ "capacity": {
+ "value": 2
+ },
+ "clientCertEnabled": {
+ "value": false
+ },
+ "disableAadAuth": {
+ "value": false
+ },
+ "disableLocalAuth": {
+ "value": true
+ },
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module managedInstance 'br:bicep/modules/sql.managed-instance:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-sqlmiwaf'
+ params: {
+ // Required parameters
+ administratorLogin: 'adminUserName'
+ administratorLoginPassword: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ "administratorLogin": {
+ "value": "adminUserName"
+ },
+ "administratorLoginPassword": {
+ "value": "via Bicep module
+
+```bicep
+module server 'br:bicep/modules/sql.server:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-sqlswaf'
+ params: {
+ // Required parameters
+ name: 'sqlswaf'
+ // Non-required parameters
+ administratorLogin: 'adminUserName'
+ administratorLoginPassword: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ "name": {
+ "value": "sqlswaf"
+ },
+ "administratorLogin": {
+ "value": "adminUserName"
+ },
+ "administratorLoginPassword": {
+ "value": "via Bicep module
+
+```bicep
+module storageAccount 'br:bicep/modules/storage.storage-account:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-ssawaf'
+ params: {
+ // Required parameters
+ name: 'ssawaf001'
+ // Non-required parameters
+ allowBlobPublicAccess: false
+ blobServices: {
+ automaticSnapshotPolicyEnabled: true
+ containerDeleteRetentionPolicyDays: 10
+ containerDeleteRetentionPolicyEnabled: true
+ containers: [
+ {
+ enableNfsV3AllSquash: true
+ enableNfsV3RootSquash: true
+ name: 'avdscripts'
+ publicAccess: 'None'
+ roleAssignments: [
+ {
+ principalId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "ssawaf001"
+ },
+ // Non-required parameters
+ "allowBlobPublicAccess": {
+ "value": false
+ },
+ "blobServices": {
+ "value": {
+ "automaticSnapshotPolicyEnabled": true,
+ "containerDeleteRetentionPolicyDays": 10,
+ "containerDeleteRetentionPolicyEnabled": true,
+ "containers": [
+ {
+ "enableNfsV3AllSquash": true,
+ "enableNfsV3RootSquash": true,
+ "name": "avdscripts",
+ "publicAccess": "None",
+ "roleAssignments": [
+ {
+ "principalId": "via Bicep module
+
+```bicep
+module privateLinkHub 'br:bicep/modules/synapse.private-link-hub:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-splhwaf'
+ params: {
+ // Required parameters
+ name: 'splhwaf001'
+ // Non-required parameters
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "splhwaf001"
+ },
+ // Non-required parameters
+ "enableDefaultTelemetry": {
+ "value": "via Bicep module
+
+```bicep
+module workspace 'br:bicep/modules/synapse.workspace:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-swwaf'
+ params: {
+ // Required parameters
+ defaultDataLakeStorageAccountResourceId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "defaultDataLakeStorageAccountResourceId": {
+ "value": "via Bicep module
+
+```bicep
+module imageTemplate 'br:bicep/modules/virtual-machine-images.image-template:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-vmiitwaf'
+ params: {
+ // Required parameters
+ customizationSteps: [
+ {
+ restartTimeout: '10m'
+ type: 'WindowsRestart'
+ }
+ ]
+ imageSource: {
+ offer: 'Windows-11'
+ publisher: 'MicrosoftWindowsDesktop'
+ sku: 'win11-22h2-avd'
+ type: 'PlatformImage'
+ version: 'latest'
+ }
+ name: 'vmiitwaf001'
+ userMsiName: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "customizationSteps": {
+ "value": [
+ {
+ "restartTimeout": "10m",
+ "type": "WindowsRestart"
+ }
+ ]
+ },
+ "imageSource": {
+ "value": {
+ "offer": "Windows-11",
+ "publisher": "MicrosoftWindowsDesktop",
+ "sku": "win11-22h2-avd",
+ "type": "PlatformImage",
+ "version": "latest"
+ }
+ },
+ "name": {
+ "value": "vmiitwaf001"
+ },
+ "userMsiName": {
+ "value": "via Bicep module
+
+```bicep
+module connection 'br:bicep/modules/web.connection:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-wcwaf'
+ params: {
+ // Required parameters
+ displayName: 'azuremonitorlogs'
+ name: 'azuremonitor'
+ // Non-required parameters
+ api: {
+ id: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "displayName": {
+ "value": "azuremonitorlogs"
+ },
+ "name": {
+ "value": "azuremonitor"
+ },
+ // Non-required parameters
+ "api": {
+ "value": {
+ "id": "via Bicep module
+
+```bicep
+module serverfarm 'br:bicep/modules/web.serverfarm:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-wsfwaf'
+ params: {
+ // Required parameters
+ name: 'wsfwaf001'
+ sku: {
+ capacity: '1'
+ family: 'S'
+ name: 'S1'
+ size: 'S1'
+ tier: 'Standard'
+ }
+ // Non-required parameters
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "wsfwaf001"
+ },
+ "sku": {
+ "value": {
+ "capacity": "1",
+ "family": "S",
+ "name": "S1",
+ "size": "S1",
+ "tier": "Standard"
+ }
+ },
+ // Non-required parameters
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "via Bicep module
+
+```bicep
+module staticSite 'br:bicep/modules/web.static-site:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-wsswaf'
+ params: {
+ // Required parameters
+ name: 'wsswaf001'
+ // Non-required parameters
+ allowConfigFileUpdates: true
+ appSettings: {
+ foo: 'bar'
+ setting: 1
+ }
+ enableDefaultTelemetry: 'via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "wsswaf001"
+ },
+ // Non-required parameters
+ "allowConfigFileUpdates": {
+ "value": true
+ },
+ "appSettings": {
+ "value": {
+ "foo": "bar",
+ "setting": 1
+ }
+ },
+ "enableDefaultTelemetry": {
+ "value": "