diff --git a/src/bicep/add-ons/tier3/solution.parameters.json b/src/bicep/add-ons/tier3/solution.parameters.json new file mode 100644 index 000000000..d81519ae6 --- /dev/null +++ b/src/bicep/add-ons/tier3/solution.parameters.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "deployActivityLogDiagnosticSetting": { + "value": false + }, + "deployDefender": { + "value": false + }, + "deployNetworkWatcher": { + "value": false + }, + "deployPolicy": { + "value": false + }, + "emailSecurityContact": { + "value": "" + }, + "firewallResourceId": { + "value": "" + }, + "hubVirtualNetworkResourceId": { + "value": "" + }, + "identifier": { + "value": "" + }, + "logAnalyticsWorkspaceResourceId": { + "value": "" + } + } +} \ No newline at end of file diff --git a/src/bicep/data/locations-arpah.json b/src/bicep/data/locations-arpah.json new file mode 100644 index 000000000..ff2196082 --- /dev/null +++ b/src/bicep/data/locations-arpah.json @@ -0,0 +1,58 @@ +{ + "AzureCloud": { + "centralus": { + "abbreviation": "usc", + "recoveryServicesGeo": "cus", + "timeDifference": "-6:00", + "timeZone": "Central Standard Time" + }, + "eastus": { + "abbreviation": "use", + "recoveryServicesGeo": "eus", + "timeDifference": "-5:00", + "timeZone": "Eastern Standard Time" + }, + "eastus2": { + "abbreviation": "use2", + "recoveryServicesGeo": "eus2", + "timeDifference": "-5:00", + "timeZone": "Eastern Standard Time" + }, + "northcentralus": { + "abbreviation": "usnc", + "recoveryServicesGeo": "ncus", + "timeDifference": "-6:00", + "timeZone": "Central Standard Time" + }, + "southcentralus": { + "abbreviation": "ussc", + "recoveryServicesGeo": "scus", + "timeDifference": "-6:00", + "timeZone": "Central Standard Time" + }, + "westcentralus": { + "abbreviation": "uswc", + "recoveryServicesGeo": "wcus", + "timeDifference": "-7:00", + "timeZone": "Mountain Standard Time" + }, + "westus": { + "abbreviation": "usw", + "recoveryServicesGeo": "wus", + "timeDifference": "-8:00", + "timeZone": "Pacific Standard Time" + }, + "westus2": { + "abbreviation": "usw2", + "recoveryServicesGeo": "wus2", + "timeDifference": "-8:00", + "timeZone": "Pacific Standard Time" + }, + "westus3": { + "abbreviation": "usw3", + "recoveryServicesGeo": "wus3", + "timeDifference": "-7:00", + "timeZone": "Mountain Standard Time" + } + } +} \ No newline at end of file diff --git a/src/bicep/mlz-arpah.bicep b/src/bicep/mlz-arpah.bicep index 99a109f6a..0b1589d5d 100644 --- a/src/bicep/mlz-arpah.bicep +++ b/src/bicep/mlz-arpah.bicep @@ -18,7 +18,7 @@ param resourcePrefix string 'test' ]) @description('The abbreviation for the environment.') -param environmentAbbreviation string = 'dev' +param environmentAbbreviation string = 'prod' @description('The subscription ID for the Hub Network and resources. It defaults to the deployment subscription.') param hubSubscriptionId string = subscription().subscriptionId @@ -230,17 +230,17 @@ param identityNetworkSecurityGroupDiagnosticsLogs array = [ param identityNetworkSecurityGroupDiagnosticsMetrics array = [] // KEY VAULT PARAMETERS -@description('An array of Key Vault Diagnostic Logs categories to collect. See "https://learn.microsoft.com/en-us/azure/key-vault/general/logging?tabs=Vault" for valid values.') -param keyVaultDiagnosticsLogs array = [ - { - category: 'AuditEvent' - enabled: true - } - { - category: 'AzurePolicyEvaluationDetails' - enabled: true - } -] +// @description('An array of Key Vault Diagnostic Logs categories to collect. See "https://learn.microsoft.com/en-us/azure/key-vault/general/logging?tabs=Vault" for valid values.') +// param keyVaultDiagnosticsLogs array = [ +// { +// category: 'AuditEvent' +// enabled: true +// } +// { +// category: 'AzurePolicyEvaluationDetails' +// enabled: true +// } +// ] // OPERATIONS PARAMETERS @@ -338,10 +338,10 @@ param deployBastion bool = true param deployAzureGatewaySubnet bool = false @description('When set to "true", provisions Windows Virtual Machine Host only. It defaults to "false".') -param deployWindowsVirtualMachine bool = true +param deployWindowsVirtualMachine bool = false -@description('When set to "true", provisions Linux Virtual Machine Host only. It defaults to "false".') -param deployLinuxVirtualMachine bool = false +// @description('When set to "true", provisions Linux Virtual Machine Host only. It defaults to "false".') +// param deployLinuxVirtualMachine bool = false @description('The CIDR Subnet Address Prefix for the Azure Bastion Subnet. It must be in the Hub Virtual Network space "hubVirtualNetworkAddressPrefix" parameter value. It must be /27 or larger.') param bastionHostSubnetAddressPrefix string = '10.0.128.192/26' @@ -357,57 +357,57 @@ param hybridUseBenefit bool = false // LINUX VIRTUAL MACHINE PARAMETERS -@description('The administrator username for the Linux Virtual Machine to Azure Bastion remote into. It defaults to "azureuser".') -param linuxVmAdminUsername string = 'azureuser' - -@allowed([ - 'sshPublicKey' - 'password' -]) -@description('[sshPublicKey/password] The authentication type for the Linux Virtual Machine to Azure Bastion remote into. It defaults to "password".') -param linuxVmAuthenticationType string = 'password' - -@minLength(12) -@secure() -@description('The administrator password or public SSH key for the Linux Virtual Machine to Azure Bastion remote into. See https://docs.microsoft.com/en-us/azure/virtual-machines/linux/faq#what-are-the-password-requirements-when-creating-a-vm- for password requirements.') -param linuxVmAdminPasswordOrKey string = deployLinuxVirtualMachine ? '' : newGuid() - -@description('The disk creation option of the Linux Virtual Machine to Azure Bastion remote into. It defaults to "FromImage".') -param linuxVmOsDiskCreateOption string = 'FromImage' - -@description('The disk type of the Linux Virtual Machine to Azure Bastion remote into. It defaults to "Standard_LRS".') -param linuxVmOsDiskType string = 'Standard_LRS' - -@allowed([ - 'Canonical' - 'RedHat' - 'Debian' -]) -@description('[Canonical for Ubuntu/RedHat/Debian] The available Linux Publishers') -param linuxVmImagePublisher string = 'Canonical' - -@allowed([ - 'ubuntuserver' - '0001-com-ubuntu-server-focal' - '0001-com-ubuntu-server-jammy' - 'RHEL' - 'Debian-12' -]) -@description('[Ubuntu/RHEL/Debian-12] The available Linux Offers') -param linuxVmImageOffer string = '0001-com-ubuntu-server-focal' - -@description('The SKU of the Linux marketplace image.') -param linuxVmImageSku string = '20_04-lts-gen2' - -@description('The size of the Linux virtual machine.') -param linuxVmSize string = 'Standard_D2s_v3' - -@allowed([ - 'Static' - 'Dynamic' -]) -@description('[Static/Dynamic] The public IP Address allocation method for the Linux virtual machine. It defaults to "Dynamic".') -param linuxNetworkInterfacePrivateIPAddressAllocationMethod string = 'Dynamic' +// @description('The administrator username for the Linux Virtual Machine to Azure Bastion remote into. It defaults to "azureuser".') +// param linuxVmAdminUsername string = 'azureuser' + +// @allowed([ +// 'sshPublicKey' +// 'password' +// ]) +// @description('[sshPublicKey/password] The authentication type for the Linux Virtual Machine to Azure Bastion remote into. It defaults to "password".') +// param linuxVmAuthenticationType string = 'password' + +// @minLength(12) +// @secure() +// @description('The administrator password or public SSH key for the Linux Virtual Machine to Azure Bastion remote into. See https://docs.microsoft.com/en-us/azure/virtual-machines/linux/faq#what-are-the-password-requirements-when-creating-a-vm- for password requirements.') +// param linuxVmAdminPasswordOrKey string = deployLinuxVirtualMachine ? '' : newGuid() + +// @description('The disk creation option of the Linux Virtual Machine to Azure Bastion remote into. It defaults to "FromImage".') +// param linuxVmOsDiskCreateOption string = 'FromImage' + +// @description('The disk type of the Linux Virtual Machine to Azure Bastion remote into. It defaults to "Standard_LRS".') +// param linuxVmOsDiskType string = 'Standard_LRS' + +// @allowed([ +// 'Canonical' +// 'RedHat' +// 'Debian' +// ]) +// @description('[Canonical for Ubuntu/RedHat/Debian] The available Linux Publishers') +// param linuxVmImagePublisher string = 'Canonical' + +// @allowed([ +// 'ubuntuserver' +// '0001-com-ubuntu-server-focal' +// '0001-com-ubuntu-server-jammy' +// 'RHEL' +// 'Debian-12' +// ]) +// @description('[Ubuntu/RHEL/Debian-12] The available Linux Offers') +// param linuxVmImageOffer string = '0001-com-ubuntu-server-focal' + +// @description('The SKU of the Linux marketplace image.') +// param linuxVmImageSku string = '20_04-lts-gen2' + +// @description('The size of the Linux virtual machine.') +// param linuxVmSize string = 'Standard_D2s_v3' + +// @allowed([ +// 'Static' +// 'Dynamic' +// ]) +// @description('[Static/Dynamic] The public IP Address allocation method for the Linux virtual machine. It defaults to "Dynamic".') +// param linuxNetworkInterfacePrivateIPAddressAllocationMethod string = 'Dynamic' // WINDOWS VIRTUAL MACHINE PARAMETERS @@ -554,7 +554,7 @@ var networks = union([ ], deployIdentity ? [ { name: 'identity' - shortName: 'id' + shortName: 'iam' deployUniqueResources: contains([ hubSubscriptionId, operationsSubscriptionId, sharedServicesSubscriptionId ], identitySubscriptionId) ? false : true subscriptionId: identitySubscriptionId nsgDiagLogs: identityNetworkSecurityGroupDiagnosticsLogs @@ -630,17 +630,17 @@ module networking 'modules/networking.bicep' = { // CUSTOMER MANAGED KEYS -module customerManagedKeys 'modules/customer-managed-keys.bicep' = { +module customerManagedKeys 'modules/customer-managed-keys-arpah.bicep' = { name: 'deploy-cmk-hub-${deploymentNameSuffix}' params: { deploymentNameSuffix: deploymentNameSuffix - environmentAbbreviation: environmentAbbreviation - keyVaultPrivateDnsZoneResourceId: networking.outputs.privateDnsZoneResourceIds.keyVault + // environmentAbbreviation: environmentAbbreviation + // keyVaultPrivateDnsZoneResourceId: networking.outputs.privateDnsZoneResourceIds.keyVault location: location mlzTags: logic.outputs.mlzTags - resourceAbbreviations: logic.outputs.resourceAbbreviations + //resourceAbbreviations: logic.outputs.resourceAbbreviations resourceGroupName: filter(resourceGroups.outputs.names, name => contains(name, 'hub'))[0] - subnetResourceId: networking.outputs.hubSubnetResourceId + //subnetResourceId: networking.outputs.hubSubnetResourceId tags: tags tier: filter(logic.outputs.tiers, tier => tier.name == 'hub')[0] tokens: logic.outputs.tokens @@ -670,7 +670,7 @@ module monitoring 'modules/monitoring.bicep' = { // REMOTE ACCESS -module remoteAccess 'modules/remote-access.bicep' = { +module remoteAccess 'modules/remote-access-arpah.bicep' = { name: 'deploy-remote-access-${deploymentNameSuffix}' params: { bastionHostPublicIPAddressAllocationMethod: 'Static' @@ -678,24 +678,24 @@ module remoteAccess 'modules/remote-access.bicep' = { bastionHostPublicIPAddressSkuName: 'Standard' bastionHostSubnetResourceId: networking.outputs.bastionHostSubnetResourceId deployBastion: deployBastion - deployLinuxVirtualMachine: deployLinuxVirtualMachine + //deployLinuxVirtualMachine: deployLinuxVirtualMachine deployWindowsVirtualMachine: deployWindowsVirtualMachine - diskEncryptionSetResourceId: customerManagedKeys.outputs.diskEncryptionSetResourceId + //diskEncryptionSetResourceId: customerManagedKeys.outputs.diskEncryptionSetResourceId hub: filter(logic.outputs.tiers, tier => tier.name == 'hub')[0] hubNetworkSecurityGroupResourceId: networking.outputs.hubNetworkSecurityGroupResourceId hubResourceGroupName: filter(resourceGroups.outputs.names, name => contains(name, 'hub'))[0] hubSubnetResourceId: networking.outputs.hubSubnetResourceId hybridUseBenefit: hybridUseBenefit - linuxNetworkInterfacePrivateIPAddressAllocationMethod: linuxNetworkInterfacePrivateIPAddressAllocationMethod - linuxVmAdminPasswordOrKey: linuxVmAdminPasswordOrKey - linuxVmAdminUsername: linuxVmAdminUsername - linuxVmImagePublisher: linuxVmImagePublisher - linuxVmImageOffer: linuxVmImageOffer - linuxVmImageSku: linuxVmImageSku - linuxVmSize: linuxVmSize - linuxVmAuthenticationType: linuxVmAuthenticationType - linuxVmOsDiskCreateOption: linuxVmOsDiskCreateOption - linuxVmOsDiskType: linuxVmOsDiskType + // linuxNetworkInterfacePrivateIPAddressAllocationMethod: linuxNetworkInterfacePrivateIPAddressAllocationMethod + // linuxVmAdminPasswordOrKey: linuxVmAdminPasswordOrKey + // linuxVmAdminUsername: linuxVmAdminUsername + // linuxVmImagePublisher: linuxVmImagePublisher + // linuxVmImageOffer: linuxVmImageOffer + // linuxVmImageSku: linuxVmImageSku + // linuxVmSize: linuxVmSize + // linuxVmAuthenticationType: linuxVmAuthenticationType + // linuxVmOsDiskCreateOption: linuxVmOsDiskCreateOption + // linuxVmOsDiskType: linuxVmOsDiskType location: location logAnalyticsWorkspaceId: monitoring.outputs.logAnalyticsWorkspaceResourceId mlzTags: logic.outputs.mlzTags @@ -720,21 +720,21 @@ module remoteAccess 'modules/remote-access.bicep' = { // STORAGE FOR LOGGING -module storage 'modules/storage.bicep' = { +module storage 'modules/storage-arpah.bicep' = { name: 'deploy-log-storage-${deploymentNameSuffix}' params: { blobsPrivateDnsZoneResourceId: networking.outputs.privateDnsZoneResourceIds.blob //deployIdentity: deployIdentity deploymentNameSuffix: deploymentNameSuffix filesPrivateDnsZoneResourceId: networking.outputs.privateDnsZoneResourceIds.file - keyVaultUri: customerManagedKeys.outputs.keyVaultUri + //keyVaultUri: customerManagedKeys.outputs.keyVaultUri location: location logStorageSkuName: logStorageSkuName mlzTags: logic.outputs.mlzTags queuesPrivateDnsZoneResourceId: networking.outputs.privateDnsZoneResourceIds.queue resourceGroupNames: resourceGroups.outputs.names - serviceToken: logic.outputs.tokens.service - storageEncryptionKeyName: customerManagedKeys.outputs.storageKeyName + //serviceToken: logic.outputs.tokens.service + //storageEncryptionKeyName: customerManagedKeys.outputs.storageKeyName tablesPrivateDnsZoneResourceId: networking.outputs.privateDnsZoneResourceIds.table tags: tags tiers: logic.outputs.tiers @@ -747,7 +747,7 @@ module storage 'modules/storage.bicep' = { // DIAGONSTIC LOGGING -module diagnostics 'modules/diagnostics.bicep' = { +module diagnostics 'modules/diagnostics-arpah.bicep' = { name: 'deploy-resource-diag-${deploymentNameSuffix}' params: { bastionDiagnosticsLogs: bastionDiagnosticsLogs @@ -755,8 +755,8 @@ module diagnostics 'modules/diagnostics.bicep' = { deploymentNameSuffix: deploymentNameSuffix firewallDiagnosticsLogs: firewallDiagnosticsLogs firewallDiagnosticsMetrics: firewallDiagnosticsMetrics - keyVaultName: customerManagedKeys.outputs.keyVaultName - keyVaultDiagnosticLogs: keyVaultDiagnosticsLogs + //keyVaultName: customerManagedKeys.outputs.keyVaultName + //keyVaultDiagnosticLogs: keyVaultDiagnosticsLogs logAnalyticsWorkspaceResourceId: monitoring.outputs.logAnalyticsWorkspaceResourceId publicIPAddressDiagnosticsLogs: publicIPAddressDiagnosticsLogs publicIPAddressDiagnosticsMetrics: publicIPAddressDiagnosticsMetrics @@ -802,7 +802,7 @@ module defenderforClouds 'modules/defender-for-clouds.bicep' = } output azureFirewallResourceId string = networking.outputs.azureFirewallResourceId -output diskEncryptionSetResourceId string = customerManagedKeys.outputs.diskEncryptionSetResourceId +//output diskEncryptionSetResourceId string = customerManagedKeys.outputs.diskEncryptionSetResourceId output hubVirtualNetworkResourceId string = networking.outputs.hubVirtualNetworkResourceId output identitySubnetResourceId string = networking.outputs.identitySubnetResourceId output locationProperties object = logic.outputs.locationProperties diff --git a/src/bicep/modules/bastion-host-arpah.bicep b/src/bicep/modules/bastion-host-arpah.bicep new file mode 100644 index 000000000..ad2a90a55 --- /dev/null +++ b/src/bicep/modules/bastion-host-arpah.bicep @@ -0,0 +1,58 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT License. +*/ + +param bastionHostSubnetResourceId string +param location string +param mlzTags object +param name string +param publicIPAddressAllocationMethod string +param publicIPAddressAvailabilityZones array +param publicIPAddressName string +param publicIPAddressSkuName string +param tags object + +resource publicIPAddress 'Microsoft.Network/publicIPAddresses@2021-02-01' = { + name: publicIPAddressName + location: location + tags: union(contains(tags, 'Microsoft.Network/publicIPAddresses') ? tags['Microsoft.Network/publicIPAddresses'] : {}, mlzTags) + sku: { + name: publicIPAddressSkuName + } + properties: { + publicIPAllocationMethod: publicIPAddressAllocationMethod + } + zones: publicIPAddressAvailabilityZones +} + +resource bastionHost 'Microsoft.Network/bastionHosts@2024-03-01' = { + name: name + location: location + tags: union(contains(tags, 'Microsoft.Network/bastionHosts') ? tags['Microsoft.Network/bastionHosts'] : {}, mlzTags) + properties: { + disableCopyPaste: false + enableFileCopy: true + enableIpConnect: true + enableKerberos: false + enableSessionRecording: true + enableShareableLink: true + + ipConfigurations: [ + { + name: 'ipconfig' + properties: { + subnet: { + id: bastionHostSubnetResourceId + } + publicIPAddress: { + id: publicIPAddress.id + } + } + } + ] + } + sku: { + name: 'Premium' + } +} diff --git a/src/bicep/modules/customer-managed-keys-arpah.bicep b/src/bicep/modules/customer-managed-keys-arpah.bicep new file mode 100644 index 000000000..51ec04015 --- /dev/null +++ b/src/bicep/modules/customer-managed-keys-arpah.bicep @@ -0,0 +1,69 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT License. +*/ + +targetScope = 'subscription' + +param deploymentNameSuffix string +// param environmentAbbreviation string +// param keyVaultPrivateDnsZoneResourceId string +param location string +param mlzTags object +//param resourceAbbreviations object +param resourceGroupName string +// param subnetResourceId string +param tags object +param tier object +param tokens object +param workloadShortName string + +// module keyVault 'key-vault-arpah.bicep' = { +// name: 'deploy-kv-${workloadShortName}-${deploymentNameSuffix}' +// scope: resourceGroup(tier.subscriptionId, resourceGroupName) +// params: { +// environmentAbbreviation: environmentAbbreviation +// keyVaultPrivateDnsZoneResourceId: keyVaultPrivateDnsZoneResourceId +// location: location +// mlzTags: mlzTags +// //resourceAbbreviations: resourceAbbreviations +// subnetResourceId: subnetResourceId +// tags: tags +// tier: tier +// tokens: tokens +// } +// } + +// module diskEncryptionSet 'disk-encryption-set.bicep' = { +// name: 'deploy-des-${workloadShortName}-${deploymentNameSuffix}' +// scope: resourceGroup(tier.subscriptionId, resourceGroupName) +// params: { +// deploymentNameSuffix: deploymentNameSuffix +// diskEncryptionSetName: tier.namingConvention.diskEncryptionSet +// keyUrl: keyVault.outputs.keyUriWithVersion +// keyVaultResourceId: keyVault.outputs.keyVaultResourceId +// location: location +// mlzTags: mlzTags +// tags: contains(tags, 'Microsoft.Compute/diskEncryptionSets') ? tags['Microsoft.Compute/diskEncryptionSets'] : {} +// workloadShortName: workloadShortName +// } +// } + +module userAssignedIdentity 'user-assigned-identity-arpah.bicep' = { + name: 'deploy-id-${workloadShortName}-${deploymentNameSuffix}' + scope: resourceGroup(tier.subscriptionId, resourceGroupName) + params: { + //keyVaultName: keyVault.outputs.keyVaultName + location: location + mlzTags: mlzTags + tags: tags + userAssignedIdentityName: replace(tier.namingConvention.userAssignedIdentity, '-${tokens.service}', '') + } +} + +// output diskEncryptionSetResourceId string = diskEncryptionSet.outputs.resourceId +// output keyVaultName string = keyVault.outputs.keyVaultName +// output keyVaultUri string = keyVault.outputs.keyVaultUri +// output keyVaultResourceId string = keyVault.outputs.keyVaultResourceId +// output storageKeyName string = keyVault.outputs.storageKeyName +output userAssignedIdentityResourceId string = userAssignedIdentity.outputs.resourceId diff --git a/src/bicep/modules/diagnostics-arpah.bicep b/src/bicep/modules/diagnostics-arpah.bicep new file mode 100644 index 000000000..bfa5abf15 --- /dev/null +++ b/src/bicep/modules/diagnostics-arpah.bicep @@ -0,0 +1,137 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT License. +*/ + +targetScope = 'subscription' + +param bastionDiagnosticsLogs array +param deployBastion bool +param deploymentNameSuffix string +param firewallDiagnosticsLogs array +param firewallDiagnosticsMetrics array +//param keyVaultDiagnosticLogs array +//param keyVaultName string +param logAnalyticsWorkspaceResourceId string +param publicIPAddressDiagnosticsLogs array +param publicIPAddressDiagnosticsMetrics array +param resourceGroupNames array +param serviceToken string +param storageAccountResourceIds array +param supportedClouds array +param tiers array + +var hub = (filter(tiers, tier => tier.name == 'hub'))[0] +var hubResourceGroupName = filter(resourceGroupNames, name => contains(name, 'hub'))[0] +var operations = first(filter(tiers, tier => tier.name == 'operations')) +var operationsResourceGroupName = filter(resourceGroupNames, name => contains(name, 'operations'))[0] +var publicIPAddresses = union([ + { + name: hub.namingConvention.azureFirewallClientPublicIPAddress + diagName: hub.namingConvention.azureFirewallClientPublicIPAddressDiagnosticSetting + } + { + name: hub.namingConvention.azureFirewallManagementPublicIPAddress + diagName: hub.namingConvention.azureFirewallManagementPublicIPAddressDiagnosticSetting + } +], deployBastion ? [ + { + name: hub.namingConvention.bastionHostPublicIPAddress + diagName: hub.namingConvention.bastionHostPublicIPAddressDiagnosticSetting + } +] : []) + +module activityLogDiagnosticSettings 'activity-log-diagnostic-settings.bicep' = [for (tier, i) in tiers: if (tier.deployUniqueResources) { + name: 'deploy-activity-diags-${tier.name}-${deploymentNameSuffix}' + scope: subscription(tier.subscriptionId) + params: { + logAnalyticsWorkspaceId: logAnalyticsWorkspaceResourceId + } +}] + +module logAnalyticsWorkspaceDiagnosticSetting 'log-analytics-diagnostic-setting.bicep' = { + name: 'deploy-law-diag-${deploymentNameSuffix}' + scope: resourceGroup(operations.subscriptionId, operationsResourceGroupName) + params: { + diagnosticStorageAccountName: split(storageAccountResourceIds[1], '/')[8] + logAnalyticsWorkspaceDiagnosticSettingName: operations.namingConvention.logAnalyticsWorkspaceDiagnosticSetting + logAnalyticsWorkspaceName: split(logAnalyticsWorkspaceResourceId, '/')[8] + supportedClouds: supportedClouds + } +} + +module networkSecurityGroupDiagnostics '../modules/network-security-group-diagnostics.bicep' = [for (tier, i) in tiers: { + name: 'deploy-nsg-diags-${tier.name}-${deploymentNameSuffix}' + scope: resourceGroup(tier.subscriptionId, resourceGroupNames[i]) + params: { + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId + logs: tier.nsgDiagLogs + logStorageAccountResourceId: storageAccountResourceIds[i] + metrics: tier.nsgDiagMetrics + networkSecurityGroupDiagnosticSettingName: tier.namingConvention.networkSecurityGroupDiagnosticSetting + networkSecurityGroupName: tier.namingConvention.networkSecurityGroup + } +}] + +module virtualNetworkDiagnostics '../modules/virtual-network-diagnostics.bicep' = [for (tier, i) in tiers: { + name: 'deploy-vnet-diags-${tier.name}-${deploymentNameSuffix}' + scope: resourceGroup(tier.subscriptionId, resourceGroupNames[i]) + params: { + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId + logs: tier.vnetDiagLogs + logStorageAccountResourceId: storageAccountResourceIds[i] + metrics: tier.vnetDiagMetrics + virtualNetworkDiagnosticSettingName: tier.namingConvention.virtualNetworkDiagnosticSetting + virtualNetworkName: tier.namingConvention.virtualNetwork + } +}] + +module publicIpAddressDiagnostics '../modules/public-ip-address-diagnostics.bicep' = [for publicIPAddress in publicIPAddresses: { + name: 'deploy-pip-diags-${split(publicIPAddress.name, '-')[2]}-${split(publicIPAddress.name, '-')[3]}-${deploymentNameSuffix}' + scope: resourceGroup(hub.subscriptionId, hubResourceGroupName) + params: { + hubStorageAccountResourceId: storageAccountResourceIds[0] + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId + publicIPAddressDiagnosticSettingName: publicIPAddress.diagName + publicIPAddressDiagnosticsLogs: publicIPAddressDiagnosticsLogs + publicIPAddressDiagnosticsMetrics: publicIPAddressDiagnosticsMetrics + publicIPAddressName: publicIPAddress.name + } +}] + +module firewallDiagnostics '../modules/firewall-diagnostics.bicep' = { + name: 'deploy-afw-diags-${deploymentNameSuffix}' + scope: resourceGroup(hub.subscriptionId, hubResourceGroupName) + params: { + firewallDiagnosticSettingsName: hub.namingConvention.azureFirewallDiagnosticSetting + firewallName: hub.namingConvention.azureFirewall + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId + logs: firewallDiagnosticsLogs + logStorageAccountResourceId: storageAccountResourceIds[0] + metrics: firewallDiagnosticsMetrics + } +} + +// module keyvaultDiagnostics '../modules/key-vault-diagnostics.bicep' = { +// name: 'deploy-kv-diags-${deploymentNameSuffix}' +// scope: resourceGroup(hub.subscriptionId, hubResourceGroupName) +// params: { +// keyVaultDiagnosticSettingName: replace(hub.namingConvention.keyVaultDiagnosticSetting, serviceToken, '') +// keyVaultName: keyVaultName +// keyVaultStorageAccountId: storageAccountResourceIds[0] +// logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId +// logs: keyVaultDiagnosticLogs +// } +// } + +module bastionDiagnostics '../modules/bastion-diagnostics.bicep' = if (deployBastion) { + name: 'deploy-bastion-diags-${deploymentNameSuffix}' + scope: resourceGroup(hub.subscriptionId, hubResourceGroupName) + params: { + bastionDiagnosticSettingName: replace(hub.namingConvention.bastionHostPublicIPAddressDiagnosticSetting, serviceToken, '') + bastionName: hub.namingConvention.bastionHost + bastionStorageAccountId: storageAccountResourceIds[0] + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId + logs: bastionDiagnosticsLogs + } +} diff --git a/src/bicep/modules/firewall-arpah.bicep b/src/bicep/modules/firewall-arpah.bicep new file mode 100644 index 000000000..4724311db --- /dev/null +++ b/src/bicep/modules/firewall-arpah.bicep @@ -0,0 +1,219 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT License. +*/ + +param clientIpConfigurationSubnetResourceId string +param clientIpConfigurationPublicIPAddressResourceId string +param dnsServers array +param enableProxy bool +param firewallPolicyName string +param firewallSupernetIPAddress string +@allowed([ + 'Alert' + 'Deny' + 'Off' +]) +param intrusionDetectionMode string +param location string +param managementIpConfigurationSubnetResourceId string +param managementIpConfigurationPublicIPAddressResourceId string +param mlzTags object +param name string +@allowed([ + 'Standard' + 'Premium' + 'Basic' +]) +param skuTier string +param tags object = {} +@allowed([ + 'Alert' + 'Deny' + 'Off' +]) +param threatIntelMode string + +var intrusionDetectionObject = { + mode: intrusionDetectionMode +} + +var dnsSettings = { + enableProxy: enableProxy + servers: dnsServers +} + +resource firewallPolicy 'Microsoft.Network/firewallPolicies@2021-02-01' = { + name: firewallPolicyName + location: location + tags: union(contains(tags, 'Microsoft.Network/firewallPolicies') ? tags['Microsoft.Network/firewallPolicies'] : {}, mlzTags) + properties: { + threatIntelMode: threatIntelMode + intrusionDetection: ((skuTier == 'Premium') ? intrusionDetectionObject : null) + sku: { + tier: skuTier + } + //dnsSettings: null + dnsSettings: ((skuTier == 'Premium' || skuTier == 'Standard') ? dnsSettings : null) + } +} + +resource firewallAppRuleCollectionGroup 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2021-02-01' = { + parent: firewallPolicy + name: 'DefaultApplicationRuleCollectionGroup' + properties: { + priority: 300 + ruleCollections: [ + { + ruleCollectionType: 'FirewallPolicyFilterRuleCollection' + action: { + type: 'Allow' + } + rules: [ + { + ruleType: 'ApplicationRule' + name: 'msftauth' + protocols: [ + { + protocolType: 'Https' + port: 443 + } + ] + fqdnTags: [] + webCategories: [] + targetFqdns: [ + 'aadcdn.msftauth.net' + 'aadcdn.msauth.net' + ] + targetUrls: [] + terminateTLS: false + sourceAddresses: [ + '*' + ] + destinationAddresses: [] + sourceIpGroups: [] + } + ] + name: 'AzureAuth' + priority: 110 + } + ] + } +} + +resource firewallNetworkRuleCollectionGroup 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2021-02-01' = { + parent: firewallPolicy + name: 'DefaultNetworkRuleCollectionGroup' + dependsOn: [ + firewallAppRuleCollectionGroup + ] + properties: { + priority: 200 + ruleCollections: [ + { + ruleCollectionType: 'FirewallPolicyFilterRuleCollection' + action: { + type: 'Allow' + } + rules: [ + { + ruleType: 'NetworkRule' + name: 'AzureCloud' + ipProtocols: [ + 'Any' + ] + sourceAddresses: [ + '*' + ] + sourceIpGroups: [] + destinationAddresses: [ + 'AzureCloud' + ] + destinationIpGroups: [] + destinationFqdns: [] + destinationPorts: [ + '*' + ] + } + ] + name: 'AllowAzureCloud' + priority: 100 + } + { + ruleCollectionType: 'FirewallPolicyFilterRuleCollection' + action: { + type: 'Allow' + } + rules: [ + { + ruleType: 'NetworkRule' + name: 'AllSpokeTraffic' + ipProtocols: [ + 'Any' + ] + sourceAddresses: [ + firewallSupernetIPAddress + ] + sourceIpGroups: [] + destinationAddresses: [ + '*' + ] + destinationIpGroups: [] + destinationFqdns: [] + destinationPorts: [ + '*' + ] + } + ] + name: 'AllowTrafficBetweenSpokes' + priority: 200 + } + ] + } +} + +resource firewall 'Microsoft.Network/azureFirewalls@2021-02-01' = { + name: name + location: location + tags: union(contains(tags, 'Microsoft.Network/azureFirewalls') ? tags['Microsoft.Network/azureFirewalls'] : {}, mlzTags) + dependsOn: [ + firewallNetworkRuleCollectionGroup + firewallAppRuleCollectionGroup + ] + properties: { + ipConfigurations: [ + { + name: 'ipconfig-client' + properties: { + subnet: { + id: clientIpConfigurationSubnetResourceId + } + publicIPAddress: { + id: clientIpConfigurationPublicIPAddressResourceId + } + } + } + ] + managementIpConfiguration: { + name: 'ipconfig-management' + properties: { + subnet: { + id: managementIpConfigurationSubnetResourceId + } + publicIPAddress: { + id: managementIpConfigurationPublicIPAddressResourceId + } + } + } + firewallPolicy: { + id: firewallPolicy.id + } + sku: { + tier: skuTier + } + } +} + +output name string = firewall.name +output privateIPAddress string = firewall.properties.ipConfigurations[0].properties.privateIPAddress +output resourceId string = firewall.id diff --git a/src/bicep/modules/hub-network-arpah.bicep b/src/bicep/modules/hub-network-arpah.bicep new file mode 100644 index 000000000..c0d94f8eb --- /dev/null +++ b/src/bicep/modules/hub-network-arpah.bicep @@ -0,0 +1,337 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT License. +*/ + +param bastionHostNetworkSecurityGroup string +param bastionHostSubnetAddressPrefix string +param azureGatewaySubnetAddressPrefix string +param deployNetworkWatcher bool +param deployBastion bool +param deployAzureGatewaySubnet bool +param dnsServers array +param enableProxy bool +param firewallClientPrivateIpAddress string +param firewallClientPublicIPAddressAvailabilityZones array +param firewallClientPublicIPAddressName string +param firewallClientSubnetAddressPrefix string +@allowed([ + 'Alert' + 'Deny' + 'Off' +]) +param firewallIntrusionDetectionMode string +param firewallManagementPublicIPAddressAvailabilityZones array +param firewallManagementPublicIPAddressName string +param firewallManagementSubnetAddressPrefix string +param firewallName string +param firewallPolicyName string +param firewallSkuTier string +param firewallSupernetIPAddress string +@allowed([ + 'Alert' + 'Deny' + 'Off' +]) +param firewallThreatIntelMode string +param location string +param mlzTags object +param networkSecurityGroupName string +param networkSecurityGroupRules array +param networkWatcherName string +param routeTableName string +param subnetAddressPrefix string +param subnetName string +param tags object +param virtualNetworkAddressPrefix string +param virtualNetworkName string +param vNetDnsServers array + +var subnets = union([ + { + name: 'AzureFirewallSubnet' + properties: { + addressPrefix: firewallClientSubnetAddressPrefix + } + } + { + name: 'AzureFirewallManagementSubnet' + properties: { + addressPrefix: firewallManagementSubnetAddressPrefix + } + } + { + name: subnetName + properties: { + addressPrefix: subnetAddressPrefix + networkSecurityGroup: { + id: networkSecurityGroup.outputs.id + } + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Disabled' + routeTable: { + id: routeTable.outputs.id + } + } + } +], deployBastion ? [ + { + name: 'AzureBastionSubnet' + properties: { + addressPrefix: bastionHostSubnetAddressPrefix + networkSecurityGroup: { + id: bastionNetworkSecurityGroup.outputs.id + } + } + } +] : [], deployAzureGatewaySubnet ? [ + { + name: 'GatewaySubnet' + properties: { + addressPrefix: azureGatewaySubnetAddressPrefix + } + } +] : []) + +//array for bastion nsg + +var bastionNetworkSecurityGroupRules = [ + { + name: 'AllowHttpsInBound' + properties: { + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'Internet' + destinationPortRange: '443' + destinationAddressPrefix: '*' + access: 'Allow' + priority: 120 + direction: 'Inbound' + } + } + { + name: 'AllowGatewayManagerInBound' + properties: { + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'GatewayManager' + destinationPortRange: '443' + destinationAddressPrefix: '*' + access: 'Allow' + priority: 130 + direction: 'Inbound' + } + } + { + name: 'AllowLoadBalancerInBound' + properties: { + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'AzureLoadBalancer' + destinationPortRange: '443' + destinationAddressPrefix: '*' + access: 'Allow' + priority: 140 + direction: 'Inbound' + } + } + { + name: 'AllowBastionHostCommunicationInBound' + properties: { + protocol: '*' + sourcePortRange: '*' + sourceAddressPrefix: 'VirtualNetwork' + destinationPortRanges: [ + '8080' + '5701' + ] + destinationAddressPrefix: 'VirtualNetwork' + access: 'Allow' + priority: 150 + direction: 'Inbound' + } + } + { + name: 'AllowSshRdpOutBound' + properties: { + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: '*' + destinationPortRanges: [ + '22' + '3389' + ] + destinationAddressPrefix: 'VirtualNetwork' + access: 'Allow' + priority: 120 + direction: 'Outbound' + } + } + { + name: 'AllowAzureCloudCommunicationOutBound' + properties: { + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: '*' + destinationPortRange: '443' + destinationAddressPrefix: 'AzureCloud' + access: 'Allow' + priority: 130 + direction: 'Outbound' + } + } + { + name: 'AllowBastionHostCommunicationOutBound' + properties: { + protocol: '*' + sourcePortRange: '*' + sourceAddressPrefix: 'VirtualNetwork' + destinationPortRanges: [ + '8080' + '5701' + ] + destinationAddressPrefix: 'VirtualNetwork' + access: 'Allow' + priority: 140 + direction: 'Outbound' + } + } + { + name: 'AllowGetSessionInformationOutBound' + properties: { + protocol: '*' + sourcePortRange: '*' + sourceAddressPrefix: '*' + destinationAddressPrefix: 'Internet' + destinationPortRanges: [ + '80' + '443' + ] + access: 'Allow' + priority: 150 + direction: 'Outbound' + } + } +] + +module networkSecurityGroup '../modules/network-security-group.bicep' = { + name: 'networkSecurityGroup' + params: { + location: location + mlzTags: mlzTags + name: networkSecurityGroupName + securityRules: networkSecurityGroupRules + tags: tags + } +} + + +module bastionNetworkSecurityGroup '../modules/network-security-group.bicep' = if (deployBastion) { + name: 'bastionNSG' + params: { + location: location + mlzTags: mlzTags + name: bastionHostNetworkSecurityGroup + securityRules: bastionNetworkSecurityGroupRules + tags: tags + } +} + +module routeTable '../modules/route-table.bicep' = { + name: 'routeTable' + params: { + disableBgpRoutePropagation: false + location: location + mlzTags: mlzTags + name: routeTableName + routeNextHopIpAddress: firewallClientPrivateIpAddress + tags: tags + } +} + +module networkWatcher '../modules/network-watcher.bicep' = if (deployNetworkWatcher) { + name: 'networkWatcher' + params: { + location: location + mlzTags: mlzTags + name: networkWatcherName + tags: tags + } +} + +module virtualNetwork '../modules/virtual-network.bicep' = { + name: 'virtualNetwork' + params: { + addressPrefix: virtualNetworkAddressPrefix + location: location + mlzTags: mlzTags + name: virtualNetworkName + subnets: subnets + tags: tags + vNetDnsServers: vNetDnsServers + } + dependsOn: [ + networkWatcher + ] +} + +module firewallClientPublicIPAddress '../modules/public-ip-address.bicep' = { + name: 'firewallClientPublicIPAddress' + params: { + availabilityZones: firewallClientPublicIPAddressAvailabilityZones + location: location + mlzTags: mlzTags + name: firewallClientPublicIPAddressName + publicIpAllocationMethod: 'Static' + skuName: 'Standard' + tags: tags + } +} + +module firewallManagementPublicIPAddress '../modules/public-ip-address.bicep' = { + name: 'firewallManagementPublicIPAddress' + params: { + availabilityZones: firewallManagementPublicIPAddressAvailabilityZones + location: location + mlzTags: mlzTags + name: firewallManagementPublicIPAddressName + publicIpAllocationMethod: 'Static' + skuName: 'Standard' + tags: tags + } +} + +module firewall '../modules/firewall-arpah.bicep' = { + name: 'firewall' + params: { + clientIpConfigurationPublicIPAddressResourceId: firewallClientPublicIPAddress.outputs.id + clientIpConfigurationSubnetResourceId: '${virtualNetwork.outputs.id}/subnets/AzureFirewallSubnet' + dnsServers: dnsServers + enableProxy: enableProxy + firewallPolicyName: firewallPolicyName + firewallSupernetIPAddress: firewallSupernetIPAddress + intrusionDetectionMode: firewallIntrusionDetectionMode + location: location + managementIpConfigurationPublicIPAddressResourceId: firewallManagementPublicIPAddress.outputs.id + managementIpConfigurationSubnetResourceId: '${virtualNetwork.outputs.id}/subnets/AzureFirewallManagementSubnet' + mlzTags: mlzTags + name: firewallName + skuTier: firewallSkuTier + tags: tags + threatIntelMode: firewallThreatIntelMode + } +} + +output bastionHostSubnetResourceId string = deployBastion ? virtualNetwork.outputs.subnets[3].id : '' +output dnsServers array = virtualNetwork.outputs.dnsServers +output firewallName string = firewall.outputs.name +output firewallPrivateIPAddress string = firewall.outputs.privateIPAddress +output firewallResourceId string = firewall.outputs.resourceId +output networkSecurityGroupName string = networkSecurityGroup.outputs.name +output networkSecurityGroupResourceId string = networkSecurityGroup.outputs.id +output subnetAddressPrefix string = virtualNetwork.outputs.subnets[2].properties.addressPrefix +output subnetName string = virtualNetwork.outputs.subnets[2].name +output subnetResourceId string = virtualNetwork.outputs.subnets[2].id +output virtualNetworkName string = virtualNetwork.outputs.name +output virtualNetworkResourceId string = virtualNetwork.outputs.id + diff --git a/src/bicep/modules/key-vault-arpah.bicep b/src/bicep/modules/key-vault-arpah.bicep new file mode 100644 index 000000000..74237caf2 --- /dev/null +++ b/src/bicep/modules/key-vault-arpah.bicep @@ -0,0 +1,159 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT License. +*/ + +param diskEncryptionKeyExpirationInDays int = 30 +param environmentAbbreviation string +param keyVaultPrivateDnsZoneResourceId string +param location string +param mlzTags object +//param resourceAbbreviations object +param subnetResourceId string +param tags object +param tier object +param tokens object + +var keyVaultPrivateEndpointName = replace(tier.namingConvention.keyVaultPrivateEndpoint, tokens.service, 'cmk') + +resource vault 'Microsoft.KeyVault/vaults@2022-07-01' = { + //name: '${resourceAbbreviations.keyVaults}${uniqueString(replace(tier.namingConvention.keyVault, tokens.service, 'cmk'), resourceGroup().id)}' + name: '${tier.namingConvention.keyVault}' + location: location + tags: union(contains(tags, 'Microsoft.KeyVault/vaults') ? tags['Microsoft.KeyVault/vaults'] : {}, mlzTags) + properties: { + enabledForDeployment: false + enabledForDiskEncryption: false + enabledForTemplateDeployment: false + enablePurgeProtection: true + enableRbacAuthorization: true + enableSoftDelete: true + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + ipRules: [] + virtualNetworkRules: [] + } + publicNetworkAccess: 'Disabled' + sku: { + family: 'A' + name: 'premium' + } + softDeleteRetentionInDays: environmentAbbreviation == 'dev' || environmentAbbreviation == 'test' ? 7 : 90 + tenantId: subscription().tenantId + } +} + +resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-04-01' = { + name: keyVaultPrivateEndpointName + location: location + tags: union(contains(tags, 'Microsoft.Network/privateEndpoints') ? tags['Microsoft.Network/privateEndpoints'] : {}, mlzTags) + properties: { + customNetworkInterfaceName: replace(tier.namingConvention.keyVaultNetworkInterface, tokens.service, 'cmk') + privateLinkServiceConnections: [ + { + name: keyVaultPrivateEndpointName + properties: { + privateLinkServiceId: vault.id + groupIds: [ + 'vault' + ] + } + } + ] + subnet: { + id: subnetResourceId + } + } +} + +resource privateDnsZoneGroups 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2021-08-01' = { + parent: privateEndpoint + name: vault.name + properties: { + privateDnsZoneConfigs: [ + { + name: 'ipconfig1' + properties: { + privateDnsZoneId: keyVaultPrivateDnsZoneResourceId + } + } + ] + } +} + +resource key_disks 'Microsoft.KeyVault/vaults/keys@2022-07-01' = { + parent: vault + name: 'DiskEncryptionKey' + properties: { + attributes: { + enabled: true + } + keySize: 4096 + kty: 'RSA' + rotationPolicy: { + attributes: { + expiryTime: 'P${string(diskEncryptionKeyExpirationInDays)}D' + } + lifetimeActions: [ + { + action: { + type: 'Notify' + } + trigger: { + timeBeforeExpiry: 'P10D' + } + } + { + action: { + type: 'Rotate' + } + trigger: { + timeAfterCreate: 'P${string(diskEncryptionKeyExpirationInDays - 7)}D' + } + } + ] + } + } +} + +resource key_storageAccounts 'Microsoft.KeyVault/vaults/keys@2022-07-01' = { + parent: vault + name: 'StorageEncryptionKey' + properties: { + attributes: { + enabled: true + } + keySize: 4096 + kty: 'RSA' + rotationPolicy: { + attributes: { + expiryTime: 'P${string(diskEncryptionKeyExpirationInDays)}D' + } + lifetimeActions: [ + { + action: { + type: 'Notify' + } + trigger: { + timeBeforeExpiry: 'P10D' + } + } + { + action: { + type: 'Rotate' + } + trigger: { + timeAfterCreate: 'P${string(diskEncryptionKeyExpirationInDays - 7)}D' + } + } + ] + } + } +} + +output keyUriWithVersion string = key_disks.properties.keyUriWithVersion +output keyVaultResourceId string = vault.id +output keyVaultName string = vault.name +output keyVaultUri string = vault.properties.vaultUri +output storageKeyName string = key_storageAccounts.name diff --git a/src/bicep/modules/logic-arpah.bicep b/src/bicep/modules/logic-arpah.bicep index 3692add9a..3bca6d481 100644 --- a/src/bicep/modules/logic-arpah.bicep +++ b/src/bicep/modules/logic-arpah.bicep @@ -37,7 +37,7 @@ module namingConventions 'naming-convention-arpah.bicep' = [for network in netwo location: location networkName: network.name networkShortName: network.shortName - resourcePrefix: resourcePrefix + //resourcePrefix: resourcePrefix stampIndex: stampIndex } }] diff --git a/src/bicep/modules/naming-convention-arpah.bicep b/src/bicep/modules/naming-convention-arpah.bicep index bf6707a1d..a2418605c 100644 --- a/src/bicep/modules/naming-convention-arpah.bicep +++ b/src/bicep/modules/naming-convention-arpah.bicep @@ -9,7 +9,7 @@ param environmentAbbreviation string param location string param networkName string param networkShortName string -param resourcePrefix string +//param resourcePrefix string = 'test' param stampIndex string = '' // Optional: Added to support AVD deployments param tokens object = { purpose:'purpose_token' @@ -17,7 +17,7 @@ param tokens object = { service: 'service_token' } -var locations = loadJsonContent('../data/locations.json')[environment().name] +var locations = loadJsonContent('../data/locations-arpah.json')[environment().name] var locationAbbreviation = locations[location].abbreviation var resourceAbbreviations = loadJsonContent('../data/resourceAbbreviations.json') @@ -38,9 +38,11 @@ var resourceAbbreviations = loadJsonContent('../data/resourceAbbreviations.json' */ //var namingConvention = '${toLower(resourcePrefix)}-${empty(stampIndex) ? '' : '${stampIndex}-'}${tokens.resource}-${networkName}-${locationAbbreviation}-${environmentAbbreviation}' -var namingConvention = '${tokens.resource}-${networkName}-${locationAbbreviation}-${environmentAbbreviation}' +var namingConvention = '${tokens.resource}-${networkName}-${locationAbbreviation}' var namingConventionVnet = '${tokens.resource}-arpa-h-it-${networkShortName}-${locationAbbreviation}' -var namingConvention_Service = '${toLower(resourcePrefix)}-${empty(stampIndex) ? '' : '${stampIndex}-'}${tokens.resource}-${networkName}-${tokens.service}-${locationAbbreviation}-${environmentAbbreviation}' +//var namingConvention_Service = '${toLower(resourcePrefix)}-${empty(stampIndex) ? '' : '${stampIndex}-'}${tokens.resource}-${networkName}-${tokens.service}-${locationAbbreviation}-${environmentAbbreviation}' +//var namingConvention_Service = '${tokens.resource}-${networkName}-${tokens.service}-${locationAbbreviation}-${environmentAbbreviation}' +var namingConvention_Service = '${tokens.resource}-${networkName}-${tokens.service}-${locationAbbreviation}' /* @@ -112,7 +114,8 @@ var names = { routeTable: replace(namingConvention, tokens.resource, resourceAbbreviations.routeTables) scalingPlan: replace(namingConvention, tokens.resource, resourceAbbreviations.scalingPlans) scalingPlanDiagnosticSetting: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.diagnosticSettings), tokens.service, resourceAbbreviations.scalingPlans) - storageAccount: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.storageAccounts), networkName, networkShortName) + //storageAccount: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.storageAccounts), networkName, networkShortName) + storageAccount: '${resourceAbbreviations.storageAccounts}${networkShortName}log${locationAbbreviation}' storageAccountDiagnosticSetting: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.diagnosticSettings), tokens.service, '${tokens.service}-${resourceAbbreviations.storageAccounts}') storageAccountBlobNetworkInterface: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.networkInterfaces), tokens.service, '${resourceAbbreviations.storageAccounts}-blob') storageAccountFileNetworkInterface: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.networkInterfaces), tokens.service, '${resourceAbbreviations.storageAccounts}-file') diff --git a/src/bicep/modules/remote-access-arpah.bicep b/src/bicep/modules/remote-access-arpah.bicep new file mode 100644 index 000000000..ac71a2f24 --- /dev/null +++ b/src/bicep/modules/remote-access-arpah.bicep @@ -0,0 +1,130 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT License. +*/ + +targetScope = 'subscription' + +param bastionHostPublicIPAddressAllocationMethod string +param bastionHostPublicIPAddressAvailabilityZones array +param bastionHostPublicIPAddressSkuName string +param bastionHostSubnetResourceId string +param deployBastion bool +//param deployLinuxVirtualMachine bool +param deployWindowsVirtualMachine bool +//param diskEncryptionSetResourceId string +param hub object +param hubNetworkSecurityGroupResourceId string +param hubResourceGroupName string +param hubSubnetResourceId string +param hybridUseBenefit bool +// param linuxNetworkInterfacePrivateIPAddressAllocationMethod string +// @secure() +// @minLength(12) +// param linuxVmAdminPasswordOrKey string +// param linuxVmAdminUsername string +// @allowed([ +// 'sshPublicKey' +// 'password' +// ]) +// param linuxVmAuthenticationType string +// param linuxVmImagePublisher string +// param linuxVmImageOffer string +// param linuxVmImageSku string +// param linuxVmSize string +// param linuxVmOsDiskCreateOption string +// param linuxVmOsDiskType string +param location string +param logAnalyticsWorkspaceId string +param mlzTags object +param serviceToken string +param supportedClouds array +param tags object +param windowsNetworkInterfacePrivateIPAddressAllocationMethod string +@secure() +@minLength(12) +param windowsVmAdminPassword string +param windowsVmAdminUsername string +param windowsVmCreateOption string +param windowsVmOffer string +param windowsVmPublisher string +param windowsVmSize string +param windowsVmSku string +param windowsVmStorageAccountType string +param windowsVmVersion string + +module bastionHost '../modules/bastion-host-arpah.bicep' = + if (deployBastion) { + name: 'remoteAccess-bastionHost' + scope: resourceGroup(hub.subscriptionId, hubResourceGroupName) + params: { + bastionHostSubnetResourceId: bastionHostSubnetResourceId + location: location + mlzTags: mlzTags + name: hub.namingConvention.bastionHost + publicIPAddressAllocationMethod: bastionHostPublicIPAddressAllocationMethod + publicIPAddressAvailabilityZones: bastionHostPublicIPAddressAvailabilityZones + publicIPAddressName: hub.namingConvention.bastionHostPublicIPAddress + publicIPAddressSkuName: bastionHostPublicIPAddressSkuName + tags: tags + } + } + +// module linuxVirtualMachine '../modules/linux-virtual-machine.bicep' = +// if (deployLinuxVirtualMachine) { +// name: 'remoteAccess-linuxVirtualMachine' +// scope: resourceGroup(hub.subscriptionId, hubResourceGroupName) +// params: { +// adminPasswordOrKey: linuxVmAdminPasswordOrKey +// adminUsername: linuxVmAdminUsername +// authenticationType: linuxVmAuthenticationType +// diskEncryptionSetResourceId: diskEncryptionSetResourceId +// diskName: replace(hub.namingConvention.virtualMachineDisk, serviceToken, 'remoteAccess-linux') +// location: location +// logAnalyticsWorkspaceId: logAnalyticsWorkspaceId +// mlzTags: mlzTags +// name: replace(hub.namingConvention.virtualMachine, serviceToken, 'ral') +// networkInterfaceName: replace(hub.namingConvention.virtualMachineNetworkInterface, serviceToken, 'remoteAccess-linux') +// networkSecurityGroupResourceId: hubNetworkSecurityGroupResourceId +// osDiskCreateOption: linuxVmOsDiskCreateOption +// osDiskType: linuxVmOsDiskType +// privateIPAddressAllocationMethod: linuxNetworkInterfacePrivateIPAddressAllocationMethod +// subnetResourceId: hubSubnetResourceId +// tags: tags +// supportedClouds: supportedClouds +// vmImagePublisher: linuxVmImagePublisher +// vmImageOffer: linuxVmImageOffer +// vmImageSku: linuxVmImageSku +// vmSize: linuxVmSize +// } +// } + +module windowsVirtualMachine '../modules/windows-virtual-machine-arpah.bicep' = + if (deployWindowsVirtualMachine) { + name: 'remoteAccess-windowsVirtualMachine' + scope: resourceGroup(hub.subscriptionId, hubResourceGroupName) + params: { + adminPassword: windowsVmAdminPassword + adminUsername: windowsVmAdminUsername + createOption: windowsVmCreateOption + //diskEncryptionSetResourceId: diskEncryptionSetResourceId + diskName: replace(hub.namingConvention.virtualMachineDisk, serviceToken, 'remoteAccess-windows') + hybridUseBenefit: hybridUseBenefit + location: location + logAnalyticsWorkspaceId: logAnalyticsWorkspaceId + mlzTags: mlzTags + name: replace(hub.namingConvention.virtualMachine, serviceToken, 'raw') + networkInterfaceName: replace(hub.namingConvention.virtualMachineNetworkInterface, serviceToken, 'remoteAccess-windows') + networkSecurityGroupResourceId: hubNetworkSecurityGroupResourceId + offer: windowsVmOffer + privateIPAddressAllocationMethod: windowsNetworkInterfacePrivateIPAddressAllocationMethod + publisher: windowsVmPublisher + size: windowsVmSize + sku: windowsVmSku + storageAccountType: windowsVmStorageAccountType + subnetResourceId: hubSubnetResourceId + supportedClouds: supportedClouds + tags: tags + version: windowsVmVersion + } + } diff --git a/src/bicep/modules/storage-account-arpah.bicep b/src/bicep/modules/storage-account-arpah.bicep new file mode 100644 index 000000000..6a595f3d8 --- /dev/null +++ b/src/bicep/modules/storage-account-arpah.bicep @@ -0,0 +1,147 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT License. +*/ + +param blobsPrivateDnsZoneResourceId string +param filesPrivateDnsZoneResourceId string +//param keyVaultUri string +param location string +param mlzTags object +param queuesPrivateDnsZoneResourceId string +//param serviceToken string +param skuName string +//param storageEncryptionKeyName string +param subnetResourceId string +param tablesPrivateDnsZoneResourceId string +param tags object +param tier object +param userAssignedIdentityResourceId string + +var subResources = [ + { + id: blobsPrivateDnsZoneResourceId + nic: tier.namingConvention.storageAccountBlobNetworkInterface + pe: tier.namingConvention.storageAccountBlobPrivateEndpoint + } + { + id: filesPrivateDnsZoneResourceId + nic: tier.namingConvention.storageAccountFileNetworkInterface + pe: tier.namingConvention.storageAccountFilePrivateEndpoint + } + { + id: queuesPrivateDnsZoneResourceId + nic: tier.namingConvention.storageAccountQueueNetworkInterface + pe: tier.namingConvention.storageAccountQueuePrivateEndpoint + } + { + id: tablesPrivateDnsZoneResourceId + nic: tier.namingConvention.storageAccountTableNetworkInterface + pe: tier.namingConvention.storageAccountTablePrivateEndpoint + } +] + +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = { + //name: uniqueString(replace(tier.namingConvention.storageAccount, serviceToken, 'log'), resourceGroup().id) + name: tier.namingConvention.storageAccount + location: location + tags: union(contains(tags, 'Microsoft.Storage/storageAccounts') ? tags['Microsoft.Storage/storageAccounts'] : {}, mlzTags) + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${userAssignedIdentityResourceId}': {} + } + } + kind: 'StorageV2' + sku: { + name: skuName + } + properties: { + accessTier: 'Hot' + allowBlobPublicAccess: false + allowCrossTenantReplication: false + allowedCopyScope: 'PrivateLink' + allowSharedKeyAccess: false + defaultToOAuthAuthentication: false + dnsEndpointType: 'Standard' + encryption: { + identity: { + userAssignedIdentity: userAssignedIdentityResourceId + } + // keySource: 'Microsoft.KeyVault' + // keyvaultproperties: { + // keyvaulturi: keyVaultUri + // keyname: storageEncryptionKeyName + // } + requireInfrastructureEncryption: true + services: { + blob: { + keyType: 'Account' + enabled: true + } + file: { + keyType: 'Account' + enabled: true + } + queue: { + keyType: 'Account' + enabled: true + } + table: { + keyType: 'Account' + enabled: true + } + } + } + minimumTlsVersion: 'TLS1_2' + networkAcls: { + bypass: 'AzureServices' + virtualNetworkRules: [] + ipRules: [] + defaultAction: 'Deny' + } + publicNetworkAccess: 'Disabled' + supportsHttpsTrafficOnly: true + } +} + +resource privateEndpoints 'Microsoft.Network/privateEndpoints@2023-04-01' = [for (resource, i) in subResources: { + name: resource.pe + location: location + tags: union(contains(tags, 'Microsoft.Network/privateEndpoints') ? tags['Microsoft.Network/privateEndpoints'] : {}, mlzTags) + properties: { + customNetworkInterfaceName: resource.nic + privateLinkServiceConnections: [ + { + name: resource.pe + properties: { + privateLinkServiceId: storageAccount.id + groupIds: [ + split(split(resource.id, '/')[8], '.')[1] + ] + } + } + ] + subnet: { + id: subnetResourceId + } + } +}] + +resource privateDnsZoneGroups 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2021-08-01' = [for (resource, i) in subResources: { + parent: privateEndpoints[i] + name: storageAccount.name + properties: { + privateDnsZoneConfigs: [ + { + name: 'ipconfig1' + properties: { + #disable-next-line use-resource-id-functions + privateDnsZoneId: resource.id + } + } + ] + } +}] + +output id string = storageAccount.id diff --git a/src/bicep/modules/storage-arpah.bicep b/src/bicep/modules/storage-arpah.bicep new file mode 100644 index 000000000..64e888943 --- /dev/null +++ b/src/bicep/modules/storage-arpah.bicep @@ -0,0 +1,46 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT License. +*/ + +targetScope = 'subscription' + +param blobsPrivateDnsZoneResourceId string +//param deployIdentity bool +param deploymentNameSuffix string +param filesPrivateDnsZoneResourceId string +//param keyVaultUri string +param location string +param logStorageSkuName string +param mlzTags object +param queuesPrivateDnsZoneResourceId string +param resourceGroupNames array +//param serviceToken string +//param storageEncryptionKeyName string +param tablesPrivateDnsZoneResourceId string +param tags object +param tiers array +param userAssignedIdentityResourceId string + +module storageAccount 'storage-account-arpah.bicep' = [for (tier, i) in tiers: { + name: 'deploy-storage-account-${tier.name}-${deploymentNameSuffix}' + scope: resourceGroup(tier.subscriptionId, resourceGroupNames[i]) + params: { + blobsPrivateDnsZoneResourceId: blobsPrivateDnsZoneResourceId + filesPrivateDnsZoneResourceId: filesPrivateDnsZoneResourceId + //keyVaultUri: keyVaultUri + location: location + mlzTags: mlzTags + queuesPrivateDnsZoneResourceId: queuesPrivateDnsZoneResourceId + //serviceToken: serviceToken + skuName: logStorageSkuName + //storageEncryptionKeyName: storageEncryptionKeyName + subnetResourceId: resourceId(tier.subscriptionId, resourceGroupNames[i], 'Microsoft.Network/virtualNetworks/subnets', tier.namingConvention.virtualNetwork, tier.namingConvention.subnet) + tablesPrivateDnsZoneResourceId: tablesPrivateDnsZoneResourceId + tags: tags + tier: tier + userAssignedIdentityResourceId: userAssignedIdentityResourceId + } +}] + +output storageAccountResourceIds array = [for (tier, i) in tiers: storageAccount[i].outputs.id] diff --git a/src/bicep/modules/user-assigned-identity-arpah.bicep b/src/bicep/modules/user-assigned-identity-arpah.bicep new file mode 100644 index 000000000..bf7ed61a8 --- /dev/null +++ b/src/bicep/modules/user-assigned-identity-arpah.bicep @@ -0,0 +1,32 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT License. +*/ + +//param keyVaultName string +param location string +param mlzTags object +param tags object +param userAssignedIdentityName string + +resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: userAssignedIdentityName + location: location + tags: union(contains(tags, 'Microsoft.ManagedIdentity/userAssignedIdentities') ? tags['Microsoft.ManagedIdentity/userAssignedIdentities'] : {}, mlzTags) +} + +// resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { +// name: keyVaultName +// } + +// resource roleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { +// name: guid(userAssignedIdentityName, 'e147488a-f6f5-4113-8e2d-b22465e65bf6', keyVaultName) +// scope: keyVault +// properties: { +// principalId: userAssignedIdentity.properties.principalId +// principalType: 'ServicePrincipal' +// roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6') // Key Vault Crypto Service Encryption User +// } +// } + +output resourceId string = userAssignedIdentity.id diff --git a/src/bicep/modules/windows-virtual-machine-arpah.bicep b/src/bicep/modules/windows-virtual-machine-arpah.bicep new file mode 100644 index 000000000..6c831a2c5 --- /dev/null +++ b/src/bicep/modules/windows-virtual-machine-arpah.bicep @@ -0,0 +1,216 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT License. +*/ + +@secure() +@minLength(12) +param adminPassword string +param adminUsername string +param createOption string +param dataDisks array = [] +//param diskEncryptionSetResourceId string +param diskName string +param hybridUseBenefit bool +param location string +param logAnalyticsWorkspaceId string +param mlzTags object +param name string +param networkInterfaceName string +param networkSecurityGroupResourceId string +param offer string +param privateIPAddressAllocationMethod string +param publisher string +param size string +param sku string +param storageAccountType string +param subnetResourceId string +param supportedClouds array +param tags object +param version string + +module networkInterface '../modules/network-interface.bicep' = { + name: 'remoteAccess-windowsNetworkInterface' + params: { + location: location + mlzTags: mlzTags + name: networkInterfaceName + networkSecurityGroupResourceId: networkSecurityGroupResourceId + privateIPAddressAllocationMethod: privateIPAddressAllocationMethod + subnetResourceId: subnetResourceId + tags: tags + } + } + +resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-04-01' = { + name: name + location: location + tags: union(contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, mlzTags) + identity: { + type: 'SystemAssigned' + } + properties: { + diagnosticsProfile: { + bootDiagnostics: { + enabled: false + } + } + hardwareProfile: { + vmSize: size + } + networkProfile: { + networkInterfaces: [ + { + id: networkInterface.outputs.id + properties: { + deleteOption: 'Delete' + } + } + ] + } + osProfile: { + computerName: take(name, 15) + adminUsername: adminUsername + adminPassword: adminPassword + } + securityProfile: { + uefiSettings: { + secureBootEnabled: true + vTpmEnabled: true + } + securityType: 'trustedLaunch' + encryptionAtHost: true + } + storageProfile: { + imageReference: { + publisher: publisher + offer: offer + sku: sku + version: version + } + osDisk: { + caching: 'ReadWrite' + createOption: createOption + deleteOption: 'Delete' + managedDisk: { + // diskEncryptionSet: { + // id: diskEncryptionSetResourceId + // } + storageAccountType: storageAccountType + } + name: diskName + osType: 'Windows' + } + dataDisks: dataDisks + } + licenseType: hybridUseBenefit ? 'Windows_Server' : null + } +} + +resource extension_GuestAttestation 'Microsoft.Compute/virtualMachines/extensions@2024-03-01' = { + parent: virtualMachine + name: 'GuestAttestation' + location: location + properties: { + publisher: 'Microsoft.Azure.Security.WindowsAttestation' + type: 'GuestAttestation' + typeHandlerVersion: '1.0' + autoUpgradeMinorVersion: true + enableAutomaticUpgrade: true + settings: { + AttestationConfig: { + MaaSettings: { + maaEndpoint: '' + maaTenantName: 'GuestAttestation' + } + AscSettings: { + ascReportingEndpoint: '' + ascReportingFrequency: '' + } + useCustomToken: 'false' + disableAlerts: 'false' + } + } + } +} + +resource extension_GuestConfiguration 'Microsoft.Compute/virtualMachines/extensions@2021-04-01' = { + parent: virtualMachine + name: 'AzurePolicyforWindows' + location: location + tags: union(contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, mlzTags) + properties: { + publisher: 'Microsoft.GuestConfiguration' + type: 'ConfigurationforWindows' + typeHandlerVersion: '1.0' + autoUpgradeMinorVersion: true + enableAutomaticUpgrade: true + } +} + +resource extension_NetworkWatcher 'Microsoft.Compute/virtualMachines/extensions@2021-04-01' = { + parent: virtualMachine + name: 'Microsoft.Azure.NetworkWatcher' + location: location + tags: union(contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, mlzTags) + properties: { + publisher: 'Microsoft.Azure.NetworkWatcher' + type: 'NetworkWatcherAgentWindows' + typeHandlerVersion: '1.4' + } + dependsOn: [ + extension_GuestConfiguration + ] +} + +resource extension_AzureMonitorWindowsAgent 'Microsoft.Compute/virtualMachines/extensions@2023-03-01' = if (contains(supportedClouds, environment().name)) { + parent: virtualMachine + name: 'AzureMonitorWindowsAgent' + location: location + tags: union(contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, mlzTags) + properties: { + publisher: 'Microsoft.Azure.Monitor' + type: 'AzureMonitorWindowsAgent' + typeHandlerVersion: '1.0' + autoUpgradeMinorVersion: true + enableAutomaticUpgrade: true + } +} + +resource extension_MicrosoftMonitoringAgent 'Microsoft.Compute/virtualMachines/extensions@2021-04-01' = if (!contains(supportedClouds, environment().name)) { + parent: virtualMachine + name: 'MMAExtension' + location: location + tags: union(contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, mlzTags) + properties: { + publisher: 'Microsoft.EnterpriseCloud.Monitoring' + type: 'MicrosoftMonitoringAgent' + typeHandlerVersion: '1.0' + settings: { + workspaceId: reference(logAnalyticsWorkspaceId , '2015-11-01-preview').customerId + stopOnMultipleConnections: true + } + protectedSettings: { + workspaceKey: listKeys(logAnalyticsWorkspaceId , '2015-11-01-preview').primarySharedKey + } + } + dependsOn: [ + extension_NetworkWatcher + ] +} + +resource extension_DependencyAgent 'Microsoft.Compute/virtualMachines/extensions@2021-04-01' = if (!contains(supportedClouds, environment().name)) { + parent: virtualMachine + name: 'DependencyAgentWindows' + location: location + tags: union(contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, mlzTags) + properties: { + publisher: 'Microsoft.Azure.Monitoring.DependencyAgent' + type: 'DependencyAgentWindows' + typeHandlerVersion: '9.5' + autoUpgradeMinorVersion: true + } + dependsOn: [ + extension_MicrosoftMonitoringAgent + ] +}