From ad3eae7b0a217d524fa5fe55704c37da083d1581 Mon Sep 17 00:00:00 2001 From: Mike Dzikowski Date: Fri, 29 Mar 2024 13:54:54 -0400 Subject: [PATCH] 843 tier 3 add on create UI definition with blue button deployments (#951) * initial checkin of Tier 3 UI * tier 3 updates to ui and powershell script * update tier3 readme * update readme --- .../tier3/New-AzureTier3TemplateSpec.ps1 | 20 + src/bicep/add-ons/tier3/README.md | 54 +-- src/bicep/add-ons/tier3/solution.json | 150 ++++--- src/bicep/add-ons/tier3/uiDefinition.json | 417 ++++++++++++++++++ 4 files changed, 529 insertions(+), 112 deletions(-) create mode 100644 src/bicep/add-ons/tier3/New-AzureTier3TemplateSpec.ps1 create mode 100644 src/bicep/add-ons/tier3/uiDefinition.json diff --git a/src/bicep/add-ons/tier3/New-AzureTier3TemplateSpec.ps1 b/src/bicep/add-ons/tier3/New-AzureTier3TemplateSpec.ps1 new file mode 100644 index 000000000..3ad6a7a8d --- /dev/null +++ b/src/bicep/add-ons/tier3/New-AzureTier3TemplateSpec.ps1 @@ -0,0 +1,20 @@ +[CmdletBinding(SupportsShouldProcess)] +param ( + [string]$TemplateSpecName, + + [Parameter(Mandatory)] + [string]$Location, + + [Parameter(Mandatory)] + [string]$ResourceGroupName +) + +New-AzTemplateSpec ` + -Name $TemplateSpecName ` + -ResourceGroupName $ResourceGroupName ` + -Version '1.0' ` + -Location $Location ` + -DisplayName "Mission Landing Zone - Tier 3 Workload Environment" ` + -TemplateFile '.\solution.json' ` + -UIFormDefinitionFile '.\uiDefinition.json' ` + -Force \ No newline at end of file diff --git a/src/bicep/add-ons/tier3/README.md b/src/bicep/add-ons/tier3/README.md index cca24efa7..cfd42ebdc 100644 --- a/src/bicep/add-ons/tier3/README.md +++ b/src/bicep/add-ons/tier3/README.md @@ -1,4 +1,4 @@ -# New Workload Example +# New Tier-3 Workload Example This example adds a spoke network and peers it to the Hub Virtual Network and routes traffic to the Hub Firewall. @@ -16,52 +16,14 @@ The docs on Azure virtual networking: +1. Deploy a new Tier-3 Workload Environment into `AzureCloud` or `AzureUsGovernment` from the Azure Portal: -Optional Parameters | Default | Description -------------------- | ------- | ----------- -virtualNetworkAddressPrefix | 10.0.125.0/26 | The address prefix for the network spoke vnet. -workloadName | workload | The name of the tier 3 workload -workloadLogStorageAccountNameParameter | Defaults to a calculated field | The name of the Storage Account + | Azure Commercial | Azure Government | + | ---------------- | ---------------- | + |[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fmissionlz%2Fmain%2Fsrc%2Fbicep%2Fadd-ons%2Ftier3%2Fsolution.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fmissionlz%2Fmain%2Fsrc%2Fbicep%2Fadd-ons%2Ftier3%2FuiDefinition.json) | [![Deploy to Azure Gov](https://aka.ms/deploytoazuregovbutton)](https://portal.azure.us/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fmissionlz%2Fmain%2Fsrc%2Fbicep%2Fadd-ons%2Ftier3%2Fsolution.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fmissionlz%2Fmain%2Fsrc%2Fbicep%2Fadd-ons%2Ftier3%2FuiDefinition.json) | + -### Generate MLZ Variable File -For instructions on generating 'deploymentVariables.json' using both Azure PowerShell and Azure CLI, please see the [README at the root of the examples folder](..\examples\README.md). - -Place the resulting 'deploymentVariables.json' file within the ./src/bicep/add-ons folder. - -## Deploy the example - -Once you have the Mission LZ output values, you can pass those in as parameters to this deployment. - -And deploy with `az deployment sub create` from the Azure CLI or `New-AzSubscriptionDeployment` from Azure PowerShell. - -### Deploying the new workload - -Connect to the appropriate Azure Environment and set appropriate context, see [getting started with Azure PowerShell or Azure CLI](..\examples\README.md) for help if needed. The commands below assume you are deploying in Azure Commercial and show the entire process from deploying MLZ and then adding an Azure App Service Plan post-deployment. - -```PowerShell -cd .\src\bicep -Connect-AzAccount -New-AzSubscriptionDeployment -Name contoso -TemplateFile .\mlz.bicep -resourcePrefix 'contoso' -Location 'eastus' -cd .\add-ons -(Get-AzSubscriptionDeployment -Name contoso).outputs | ConvertTo-Json | Out-File -FilePath .\deploymentVariables.json -cd .\tier3 -New-AzSubscriptionDeployment -DeploymentName deployTier3 -TemplateFile .\tier3.bicep -resourcePrefix myTier3 -Location 'eastus' -``` - -```Azure CLI -az login -cd src/bicep -az deployment sub create -n contoso -f mlz.bicep -l eastus --parameters resourcePrefix=contoso -cd add-ons -az deployment sub show -n contoso --query properties.outputs > ./deploymentVariables.json -cd tier3 -az deployment sub create -n deployTier3 -f tier3.bicep -l eastus --parameters resourcePrefix='myTier3' -``` diff --git a/src/bicep/add-ons/tier3/solution.json b/src/bicep/add-ons/tier3/solution.json index 2cb859a9b..ff1e8f739 100644 --- a/src/bicep/add-ons/tier3/solution.json +++ b/src/bicep/add-ons/tier3/solution.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13136741410504274799" + "version": "0.26.54.24096", + "templateHash": "15317263239998054719" } }, "parameters": { @@ -233,8 +233,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1875159740310035831" + "version": "0.26.54.24096", + "templateHash": "16223515126777270114" } }, "parameters": { @@ -785,8 +785,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "16214742128972496947" + "version": "0.26.54.24096", + "templateHash": "6875432534491165308" } }, "parameters": { @@ -893,8 +893,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "18429929713495132232" + "version": "0.26.54.24096", + "templateHash": "14258191516922489977" } }, "parameters": { @@ -1022,8 +1022,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "2879310497370314190" + "version": "0.26.54.24096", + "templateHash": "17369831668491029949" } }, "parameters": { @@ -1156,8 +1156,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "14595420933363835973" + "version": "0.26.54.24096", + "templateHash": "11944009476052352030" } }, "parameters": { @@ -1252,8 +1252,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17886518240477437470" + "version": "0.26.54.24096", + "templateHash": "7780881015892644264" } }, "parameters": { @@ -1336,8 +1336,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "16396784950679856617" + "version": "0.26.54.24096", + "templateHash": "3452822322028754232" } }, "parameters": { @@ -1428,8 +1428,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "7393503353750877364" + "version": "0.26.54.24096", + "templateHash": "7600987290536274187" } }, "parameters": { @@ -1509,8 +1509,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11899290391963495406" + "version": "0.26.54.24096", + "templateHash": "10147997802991299261" } }, "parameters": { @@ -1651,8 +1651,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "16252707641537943975" + "version": "0.26.54.24096", + "templateHash": "13987612441032900755" } }, "parameters": { @@ -1697,8 +1697,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13171060180511860684" + "version": "0.26.54.24096", + "templateHash": "10509951780144584720" } }, "parameters": { @@ -1763,8 +1763,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8708137600409483830" + "version": "0.26.54.24096", + "templateHash": "11212369470578362410" } }, "parameters": { @@ -1803,8 +1803,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13171060180511860684" + "version": "0.26.54.24096", + "templateHash": "10509951780144584720" } }, "parameters": { @@ -1888,8 +1888,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15773333571553716283" + "version": "0.26.54.24096", + "templateHash": "3912836360709277206" } }, "parameters": { @@ -1953,8 +1953,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5050434978606880010" + "version": "0.26.54.24096", + "templateHash": "17697959832977472677" } }, "parameters": { @@ -2144,6 +2144,10 @@ "type": "string", "value": "[resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName'))]" }, + "keyVaultName": { + "type": "string", + "value": "[parameters('keyVaultName')]" + }, "keyVaultUri": { "type": "string", "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2022-07-01').vaultUri]" @@ -2191,8 +2195,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4614698695242257266" + "version": "0.26.54.24096", + "templateHash": "8720396217971176471" } }, "parameters": { @@ -2265,8 +2269,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15536832716681446548" + "version": "0.26.54.24096", + "templateHash": "16166471121138690529" } }, "parameters": { @@ -2354,8 +2358,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4000331216612946216" + "version": "0.26.54.24096", + "templateHash": "9210810628290341713" } }, "parameters": { @@ -2406,8 +2410,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15536832716681446548" + "version": "0.26.54.24096", + "templateHash": "16166471121138690529" } }, "parameters": { @@ -2470,10 +2474,18 @@ "type": "string", "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('networkProperties').subscriptionId, parameters('networkProperties').resourceGroupName), 'Microsoft.Resources/deployments', format('deploy-disk-encryption-set_{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" }, + "KeyVaultName": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('networkProperties').subscriptionId, parameters('networkProperties').resourceGroupName), 'Microsoft.Resources/deployments', format('deploy-key-vault-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultName.value]" + }, "keyVaultUri": { "type": "string", "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('networkProperties').subscriptionId, parameters('networkProperties').resourceGroupName), 'Microsoft.Resources/deployments', format('deploy-key-vault-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultUri.value]" }, + "keyVaultResourceId": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('networkProperties').subscriptionId, parameters('networkProperties').resourceGroupName), 'Microsoft.Resources/deployments', format('deploy-key-vault-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultResourceId.value]" + }, "storageKeyName": { "type": "string", "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('networkProperties').subscriptionId, parameters('networkProperties').resourceGroupName), 'Microsoft.Resources/deployments', format('deploy-key-vault-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.storageKeyName.value]" @@ -2541,8 +2553,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "14173874211340091680" + "version": "0.26.54.24096", + "templateHash": "8737645416670201102" } }, "parameters": { @@ -2639,8 +2651,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "9733002769278931836" + "version": "0.26.54.24096", + "templateHash": "6728136650948993728" } }, "parameters": { @@ -2882,8 +2894,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "3973306502467550024" + "version": "0.26.54.24096", + "templateHash": "4413878666182449542" } }, "parameters": { @@ -2945,8 +2957,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "6094940624248562005" + "version": "0.26.54.24096", + "templateHash": "14852926421482749735" } }, "parameters": { @@ -3039,8 +3051,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11378963378628427560" + "version": "0.26.54.24096", + "templateHash": "14146627423781433804" } }, "parameters": { @@ -3111,8 +3123,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10651169427067678785" + "version": "0.26.54.24096", + "templateHash": "17731844489242416935" } }, "parameters": { @@ -3193,8 +3205,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "256355721797700603" + "version": "0.26.54.24096", + "templateHash": "2594345850908952645" } }, "parameters": { @@ -3247,8 +3259,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10054670634576893900" + "version": "0.26.54.24096", + "templateHash": "14286124867588017135" } }, "parameters": { @@ -3423,8 +3435,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15536832716681446548" + "version": "0.26.54.24096", + "templateHash": "16166471121138690529" } }, "parameters": { @@ -3508,14 +3520,19 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "3655671661053884718" + "version": "0.26.54.24096", + "templateHash": "15033542879751971590" } }, "parameters": { - "bundle": { + "defenderPlans": { "type": "array", - "defaultValue": "[if(equals(environment().name, 'AzureCloud'), createArray('Api', 'AppServices', 'Arm', 'CloudPosture', 'Containers', 'CosmosDbs', 'KeyVaults', 'OpenSourceRelationalDatabases', 'SqlServers', 'SqlServerVirtualMachines', 'StorageAccounts', 'VirtualMachines'), if(equals(environment().name, 'AzureUSGovernment'), createArray('Arm', 'Containers', 'OpenSourceRelationalDatabases', 'SqlServers', 'SqlServerVirtualMachines', 'StorageAccounts', 'VirtualMachines'), createArray()))]" + "defaultValue": [ + "VirtualMachines" + ], + "metadata": { + "description": "Defender Paid protection Plans. Even if a customer selects the free sku, at least 1 paid protection plan must be specified." + } }, "enableAutoProvisioning": { "type": "bool", @@ -3545,9 +3562,9 @@ }, "defenderSkuTier": { "type": "string", - "defaultValue": "Standard", + "defaultValue": "Free", "metadata": { - "description": "[Standard/Free] The SKU for Defender. It defaults to \"Standard\"." + "description": "[Standard/Free] The SKU for Defender. It defaults to \"Free\"." } } }, @@ -3558,13 +3575,14 @@ { "copy": { "name": "defenderPricing", - "count": "[length(parameters('bundle'))]", + "count": "[length(parameters('defenderPlans'))]", "mode": "serial", "batchSize": 1 }, + "condition": "[not(empty(parameters('defenderPlans')))]", "type": "Microsoft.Security/pricings", "apiVersion": "2023-01-01", - "name": "[parameters('bundle')[copyIndex()]]", + "name": "[parameters('defenderPlans')[copyIndex()]]", "properties": { "pricingTier": "[parameters('defenderSkuTier')]" } diff --git a/src/bicep/add-ons/tier3/uiDefinition.json b/src/bicep/add-ons/tier3/uiDefinition.json new file mode 100644 index 000000000..6d3c0c9eb --- /dev/null +++ b/src/bicep/add-ons/tier3/uiDefinition.json @@ -0,0 +1,417 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2021-09-09/uiFormDefinition.schema.json", + "view": { + "kind": "Form", + "properties": { + "title": "Mission Landing Zone Add-On: Tier 3 Workload Environment", + "steps": [ + { + "name": "basics", + "label": "Basics", + "elements": [ + { + "name": "prerequisites", + "type": "Microsoft.Common.InfoBox", + "options": { + "text": "Prior to deployment, make sure you meet the prerequisites outlined in the resource pre-reqs section in Zero Trust Image solution documentation.", + "uri": "https://github.com/mikedzikowski/ZTAImage#prequisites", + "icon": "Warning" + } + }, + { + "name": "scope", + "type": "Microsoft.Common.ResourceScope", + "instanceDetailsLabel": "", + "location": { + "resourceTypes": [] + } + }, + { + "name": "hub", + "label": "Hub Resources", + "type": "Microsoft.Common.Section", + "elements": [ + { + "name": "api", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "subscriptions?api-version=2020-01-01" + } + }, + { + "name": "subscription", + "label": "Subscription", + "type": "Microsoft.Common.DropDown", + "defaultValue": "[steps('basics').scope.subscription.displayName]", + "toolTip": "Select the subscription for your Mission Landing Zone Hub network, firewall, and remote access resources.", + "filter": true, + "constraints": { + "allowedValues": "[map(steps('basics').hub.api.value, (item) => parse(concat('{\"label\":\"', item.displayName, '\",\"value\":\"', item.id, '\",\"description\":\"', 'ID: ', item.subscriptionId, '\"}')))]", + "required": true + } + }, + { + "name": "virtualNetworksApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat(steps('basics').hub.subscription, '/providers/Microsoft.Network/virtualNetworks?api-version=2023-05-01')]" + } + }, + { + "name": "virtualNetwork", + "type": "Microsoft.Common.DropDown", + "visible": true, + "label": "Virtual network", + "defaultValue": "[filter(map(steps('basics').hub.virtualNetworksApi.value, (item) => item.name), (item) => contains(item, 'hub'))]", + "filter": true, + "toolTip": "Select the existing Hub virtual network.", + "constraints": { + "required": true, + "allowedValues": "[map(steps('basics').hub.virtualNetworksApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]" + } + }, + { + "name": "azureFirewallsApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat(steps('basics').hub.subscription, '/resourceGroups/', first(skip(split(steps('basics').hub.virtualNetwork, '/'), 4)), '/providers/Microsoft.Network/azureFirewalls?api-version=2023-09-01')]" + } + }, + { + "name": "azureFirewall", + "type": "Microsoft.Common.DropDown", + "visible": true, + "label": "Azure firewall", + "defaultValue": "[first(map(steps('basics').hub.azureFirewallsApi.value, (item) => item.name))]", + "filter": true, + "toolTip": "Select the existing Hub Azure firewall.", + "constraints": { + "required": true, + "allowedValues": "[map(steps('basics').hub.azureFirewallsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]" + } + } + ] + }, + { + "name": "naming", + "type": "Microsoft.Common.Section", + "label": "Naming Components", + "elements": [ + { + "name": "description", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "The values selected below will be used as components in your naming convention to name your Azure resource groups and resources. For more information on the naming convention used in this solution, refer to the documentation.", + "link": {} + } + }, + { + "name": "identifier", + "type": "Microsoft.Common.TextBox", + "label": "Identifier", + "toolTip": "Input a 3 character identifier for the resource group and resource names created with this solution. The identifier should represent a unique value within your organization, such as a business unit or project.", + "placeholder": "Example: ms", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,3}$", + "validationMessage": "The value must be 1 - 3 characters in length and must be alphanumeric." + } + }, + { + "name": "environment", + "type": "Microsoft.Common.DropDown", + "visible": true, + "label": "Environment Abbreviation", + "defaultValue": "Development (dev)", + "toolTip": "Select the target environment for the solution. The single letter environment abbreviation will be used as part of the naming convention for the resoure groups and resources.", + "constraints": { + "required": true, + "allowedValues": [ + { + "label": "Development (dev)", + "value": "dev" + }, + { + "label": "Production (prod)", + "value": "prod" + }, + { + "label": "Test (test)", + "value": "test" + } + ] + } + } + ] + } + ] + }, + { + "name": "networking", + "label": "Networking", + "elements": [ + { + "name": "networking", + "label": "Networking", + "type": "Microsoft.Common.Section", + "elements": [ + { + "name": "virtualNetworkAddressCidrRange", + "label": "Virtual network CIDR range", + "type": "Microsoft.Common.TextBox", + "defaultValue": "10.0.133.0/24", + "toolTip": "Specify an address CIDR range within the range [10,24].", + "constraints": { + "required": true, + "validations": [ + { + "regex": "^(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(?:\/(1[0-9]|2[0-4]))$", + "message": "Invalid CIDR range. The address prefix must be in the range [10,24]." + } + ] + } + }, + { + "name": "subnetAddressCidrRange", + "label": "Subnet CIDR range", + "type": "Microsoft.Common.TextBox", + "defaultValue": "10.0.133.0/24", + "toolTip": "Specify a CIDR range for the default subnet within the Shared Services Virtual Network range [24].", + "constraints": { + "required": true, + "validations": [ + { + "regex": "^(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(?:\/(2[4-8]))$", + "message": "Invalid CIDR range. The address prefix must be in the range [24,28]." + }, + { + "isValid": "[if(greaterOrEquals(last(split(steps('networking').networking.virtualNetworkAddressCidrRange, '/')), 8), equals(last(take(split(first(split(steps('networking').networking.virtualNetworkAddressCidrRange, '/')), '.'), 1)), last(take(split(first(split(steps('networking').networking.subnetAddressCidrRange, '/')), '.'), 1))), true)]", + "message": "CIDR range not within virtual network CIDR range (first octet)." + }, + { + "isValid": "[if(greaterOrEquals(last(split(steps('networking').networking.virtualNetworkAddressCidrRange, '/')), 16), equals(last(take(split(first(split(steps('networking').networking.virtualNetworkAddressCidrRange, '/')), '.'), 2)), last(take(split(first(split(steps('networking').networking.subnetAddressCidrRange, '/')), '.'), 2))), true)]", + "message": "CIDR range not within virtual network CIDR range (second octet)." + }, + { + "isValid": "[if(greaterOrEquals(last(split(steps('networking').networking.virtualNetworkAddressCidrRange, '/')), 24), equals(last(take(split(first(split(steps('networking').networking.virtualNetworkAddressCidrRange, '/')), '.'), 3)), last(take(split(first(split(steps('networking').networking.subnetAddressCidrRange, '/')), '.'), 3))), true)]", + "message": "CIDR range not within virtual network CIDR range (third octet)." + }, + { + "isValid": "[lessOrEquals(last(split(steps('networking').networking.virtualNetworkAddressCidrRange, '/')), last(split(steps('networking').networking.subnetAddressCidrRange, '/')))]", + "message": "CIDR range not within virtual network CIDR range (subnet mask)." + } + ] + } + }, + { + "name": "networkWatchersApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat(steps('basics').scope.subscription.id, '/providers/Microsoft.Network/networkWatchers?api-version=2023-09-01')]" + } + } + ] + } + ] + }, + { + "name": "compliance", + "label": "Compliance", + "elements": [ + { + "name": "defenderForCloud", + "label": "Defender for Cloud", + "type": "Microsoft.Common.Section", + "elements": [ + { + "name": "workspaceSettingsApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat(steps('basics').scope.subscription.id, '/providers/Microsoft.Security/workspaceSettings?api-version=2017-08-01-preview')]" + } + }, + { + "name": "deployDefender", + "type": "Microsoft.Common.CheckBox", + "label": "Enable Defender for Cloud?", + "toolTip": "Check here to to deploy defender for cloud to the target subscription.", + "constraints": { + "required": false + } + }, + { + "name": "emailSecurityContact", + "type": "Microsoft.Common.TextBox", + "label": "Email Address for Security Notifications", + "defaultValue": "", + "toolTip": "Please enter a valid email address for the security team.", + "constraints": { + "required": true, + "regex": "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", + "validationMessage": "Email is not valid. Please re-enter." + }, + "visible": "[steps('compliance').defenderForCloud.deployDefender]" + } + ] + }, + { + "name": "policySection", + "label": "Azure Policy", + "type": "Microsoft.Common.Section", + "elements": [ + { + "name": "policySubsetDetailsTextBlock", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Mission Landing Zone comes bundled with a relevant subset of available Azure policies." + } + }, + { + "name": "policyOptionalTextBlock", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Enabling policies is optional, but recommended." + } + }, + { + "name": "deployPolicy", + "type": "Microsoft.Common.CheckBox", + "label": "Deploy policy assignments?", + "toolTip": "Check here to create policy assignments for the resources created by Mission Landing Zone.", + "constraints": { + "required": false + } + }, + { + "name": "policy", + "type": "Microsoft.Common.DropDown", + "label": "Policy Assignment", + "placeholder": "", + "defaultValue": "NIST SP 800-53", + "toolTip": "DoD IL5 is only available in AzureUsGovernment and will switch to NISTRev4 if tried in AzureCloud.", + "multiselect": false, + "selectAll": false, + "filter": true, + "filterPlaceholder": "Filter items ...", + "multiLine": true, + "defaultDescription": "Select one of the bundled built-in policy assignments.", + "constraints": { + "allowedValues": [ + { + "label": "NIST SP 800-53 Rev4", + "description": "The US National Institute of Standards and Technology (NIST) publishes a catalog of security and privacy controls, Special Publication (SP) 800-53, for all federal information systems in the United States (except those related to national security).", + "value": "NISTRev4" + }, + { + "label": "NIST SP 800-53 Rev5", + "description": "The US National Institute of Standards and Technology (NIST) publishes a catalog of security and privacy controls, Special Publication (SP) 800-53, for all federal information systems in the United States (except those related to national security).", + "value": "NISTRev5" + }, + { + "label": "DoD IL5", + "description": "The Defense Information Systems Agency (DISA) is an agency of the US Department of Defense (DoD) that is responsible for developing and maintaining the DoD Cloud Computing Security Requirements Guide (SRG). These policies are only available for AzureUsGovernment and will switch to NISTRev4 if tried in AzureCloud.", + "value": "IL5" + }, + { + "label": "CMMC", + "description": "The Cybersecurity Maturity Model Certification (CMMC) is a new framework developed by the US Department of Defense (DoD) that requires formal third-party audits of defense industrial base (DIB) contractor cybersecurity practices.", + "value": "CMMC" + } + ] + }, + "visible": "[steps('compliance').policySection.deployPolicy]" + } + ] + }, + { + "name": "virtualNetworkApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat(steps('basics').hub.virtualNetwork, '?api-version=2023-09-01')]" + } + }, + { + "name": "logAnalyticsWorkspacesApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat('/subscriptions/', first(skip(split(first(map(filter(steps('compliance').virtualNetworkApi.properties.virtualNetworkPeerings, (item) => contains(item.properties.remoteVirtualNetwork.id, 'operations')), (item) => item.properties.remoteVirtualNetwork.id)), '/'), 2)), '/resourcegroups/', first(skip(split(first(map(filter(steps('compliance').virtualNetworkApi.properties.virtualNetworkPeerings, (item) => contains(item.properties.remoteVirtualNetwork.id, 'operations')), (item) => item.properties.remoteVirtualNetwork.id)), '/'), 4)), '/providers/Microsoft.OperationalInsights/workspaces?api-version=2023-09-01')]" + } + }, + { + "name": "logAnalyticsWorkspace", + "type": "Microsoft.Common.DropDown", + "visible": true, + "label": "Existing Log Analytics Workspace for Central Logging", + "defaultValue": "[first(map(steps('compliance').logAnalyticsWorkspacesApi.value, (item) => item.name))]", + "filter": true, + "toolTip": "Select the existing Hub Azure firewall.", + "constraints": { + "required": true, + "allowedValues": "[map(steps('compliance').logAnalyticsWorkspacesApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]" + } + }, + { + "name": "diagnosticSettingsApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat(steps('basics').scope.subscription.id, '/providers/Microsoft.Insights/diagnosticSettings?api-version=2021-05-01-preview')]" + } + } + ] + }, + { + "name": "tags", + "label": "Tags", + "elements": [ + { + "name": "tags", + "type": "Microsoft.Common.TagsByResource", + "resources": [ + "Microsoft.Automation/automationAccounts", + "Microsoft.Compute/galleries", + "Microsoft.Compute/virtualMachines", + "Microsoft.Insights/actionGroups", + "Microsoft.Insights/scheduledQueryRules", + "Microsoft.KeyVault/vaults", + "Microsoft.ManagedIdentity/userAssignedIdentities", + "Microsoft.Network/networkInterfaces", + "Microsoft.Network/privateEndpoints", + "Microsoft.Resources/templateSpecs" + ] + } + ] + } + ] + }, + "outputs": { + "parameters": { + "firewallResourceId": "[steps('basics').hub.azureFirewall]", + "deployActivityLogDiagnosticSetting": "[empty(steps('compliance').diagnosticSettingsApi.value)]", + "deployDefender": "[and(steps('compliance').defenderForCloud.deployDefender, empty(steps('compliance').defenderForCloud.workspaceSettingsApi.value))]", + "deployNetworkWatcher": "[empty(filter(steps('networking').networking.networkWatchersApi.value, (item) => equals(item.location, steps('basics').scope.location.name)))]", + "deployPolicy": "[steps('compliance').policySection.deployPolicy]", + "emailSecurityContact": "[if(and(steps('compliance').defenderForCloud.deployDefender, empty(steps('compliance').defenderForCloud.workspaceSettingsApi.value)), steps('compliance').defenderForCloud.emailSecurityContact, '')]", + "environmentAbbreviation": "[steps('basics').naming.environment]", + "hubVirtualNetworkResourceId": "[steps('basics').hub.virtualNetwork]", + "resourcePrefix": "[steps('basics').naming.identifier]", + "location": "[steps('basics').scope.location.name]", + "policy": "[if(steps('compliance').policySection.deployPolicy, steps('compliance').policySection.policy, '')]", + "logAnalyticsWorkspaceResourceId": "[steps('compliance').logAnalyticsWorkspace]", + "subnetAddressPrefix": "[steps('networking').networking.subnetAddressCidrRange]", + "virtualNetworkAddressPrefix": "[steps('networking').networking.virtualNetworkAddressCidrRange]", + "tags": "[steps('tags').tags]" + }, + "kind": "Subscription", + "location": "[steps('basics').scope.location.name]", + "subscriptionId": "[steps('basics').scope.subscription.id]" + } + } +} \ No newline at end of file