diff --git a/quickstart/202-machine-learning-moderately-secure-existing-VNet/.gitignore b/quickstart/202-machine-learning-moderately-secure-existing-VNet/.gitignore new file mode 100644 index 00000000..6f8b76c0 --- /dev/null +++ b/quickstart/202-machine-learning-moderately-secure-existing-VNet/.gitignore @@ -0,0 +1,37 @@ +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log + +# Ignore any .tfvars files that are generated automatically for each Terraform run. Most +# .tfvars files are managed as part of configuration and so should be included in +# version control. +# +# example.tfvars + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json +values.tfvars +*.tfvars +settings.tfvars +# Include override files you do wish to add to version control using negated pattern +# +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* +terraform/.terraform.lock.hcl +.DS_Store +terraform/.terraform.lock.hcl +terraform/.terraform.lock.hcl +.terraform.lock.hcl +terraform/.terraform.lock.hcl \ No newline at end of file diff --git a/quickstart/202-machine-learning-moderately-secure-existing-VNet/main.tf b/quickstart/202-machine-learning-moderately-secure-existing-VNet/main.tf new file mode 100644 index 00000000..67dea407 --- /dev/null +++ b/quickstart/202-machine-learning-moderately-secure-existing-VNet/main.tf @@ -0,0 +1,21 @@ +terraform { + required_version = ">=0.15.0" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "=2.76.0" + } + } +} + +provider "azurerm" { + features {} +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "default" { + name = "rg-${var.name}-${var.environment}" + location = var.location +} diff --git a/quickstart/202-machine-learning-moderately-secure-existing-VNet/network.tf b/quickstart/202-machine-learning-moderately-secure-existing-VNet/network.tf new file mode 100644 index 00000000..73caea14 --- /dev/null +++ b/quickstart/202-machine-learning-moderately-secure-existing-VNet/network.tf @@ -0,0 +1,106 @@ +# Network Security Groups + +resource "azurerm_network_security_group" "nsg-training" { + name = "nsg-training" + location = azurerm_resource_group.default.location + resource_group_name = azurerm_resource_group.default.name + + security_rule { + name = "BatchNodeManagement" + priority = 100 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "29876-29877" + source_address_prefix = "BatchNodeManagement" + destination_address_prefix = "*" + } + security_rule { + name = "AzureMachineLearning" + priority = 110 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "44224" + source_address_prefix = "AzureMachineLearning" + destination_address_prefix = "*" + } +} + +resource "azurerm_subnet_network_security_group_association" "nsg-training-link" { + subnet_id = var.training_subnet_resource_id + network_security_group_id = azurerm_network_security_group.nsg-training.id +} + +resource "azurerm_network_security_group" "nsg-aks" { + name = "nsg-aks" + location = azurerm_resource_group.default.location + resource_group_name = azurerm_resource_group.default.name + + +} + +resource "azurerm_subnet_network_security_group_association" "nsg-aks-link" { + subnet_id = var.aks_subnet_resource_id + network_security_group_id = azurerm_network_security_group.nsg-aks.id +} + +# User Defined Routes + +#UDR for Compute instance and compute clusters +resource "azurerm_route_table" "rt-training" { + name = "rt-training" + location = azurerm_resource_group.default.location + resource_group_name = azurerm_resource_group.default.name +} + +resource "azurerm_route" "training-Internet-Route" { + name = "Internet" + resource_group_name = azurerm_resource_group.default.name + route_table_name = azurerm_route_table.rt-training.name + address_prefix = "0.0.0.0/0" + next_hop_type = "Internet" +} + +resource "azurerm_route" "training-AzureMLRoute" { + name = "AzureMLRoute" + resource_group_name = azurerm_resource_group.default.name + route_table_name = azurerm_route_table.rt-training.name + address_prefix = "AzureMachineLearning" + next_hop_type = "Internet" +} + +resource "azurerm_route" "training-BatchRoute" { + name = "BatchRoute" + resource_group_name = azurerm_resource_group.default.name + route_table_name = azurerm_route_table.rt-training.name + address_prefix = "BatchNodeManagement" + next_hop_type = "Internet" +} + +resource "azurerm_subnet_route_table_association" "rt-training-link" { + subnet_id = var.training_subnet_resource_id + route_table_id = azurerm_route_table.rt-training.id +} +# Inferencing (AKS) Route + +resource "azurerm_route_table" "rt-aks" { + name = "rt-aks" + location = azurerm_resource_group.default.location + resource_group_name = azurerm_resource_group.default.name +} + +resource "azurerm_route" "aks-Internet-Route" { + name = "Internet" + resource_group_name = azurerm_resource_group.default.name + route_table_name = azurerm_route_table.rt-aks.name + address_prefix = "0.0.0.0/0" + next_hop_type = "Internet" +} + +resource "azurerm_subnet_route_table_association" "rt-aks-link" { + subnet_id = var.aks_subnet_resource_id + route_table_id = azurerm_route_table.rt-aks.id +} \ No newline at end of file diff --git a/quickstart/202-machine-learning-moderately-secure-existing-VNet/readme.md b/quickstart/202-machine-learning-moderately-secure-existing-VNet/readme.md new file mode 100644 index 00000000..b3650d2c --- /dev/null +++ b/quickstart/202-machine-learning-moderately-secure-existing-VNet/readme.md @@ -0,0 +1,63 @@ +# Azure Machine Learning workspace (moderately secure network set up) + +This deployment configuration specifies an [Azure Machine Learning workspace](https://docs.microsoft.com/en-us/azure/machine-learning/concept-workspace), +and its associated resources including Azure Key Vault, Azure Storage, Azure Application Insights and Azure Container Registry. + +In addition to these core services, this configuration specifies any networking components that are required to set up Azure Machine Learning +for private network connectivity using [Azure Private Link](https://docs.microsoft.com/en-us/azure/private-link/). + +This configuration describes the minimal set of resources you require to get started with Azure Machine Learning in a network-isolated set-up. + +To learn more about security configurations in Azure Machine Learning, see [Enterprise security and governance for Azure Machine Learning](https://docs.microsoft.com/en-us/azure/machine-learning/concept-enterprise-security). + +## Resources + +| Terraform Resource Type | Description | +| - | - | +| `azurerm_resource_group` | The resource group all resources get deployed into | +| `azurerm_application_insights` | An Azure Application Insights instance associated to the Azure Machine Learning workspace | +| `azurerm_key_vault` | An Azure Key Vault instance associated to the Azure Machine Learning workspace | +| `azurerm_storage_account` | An Azure Storage instance associated to the Azure Machine Learning workspace | +| `azurerm_container_registry` | An Azure Container Registry instance associated to the Azure Machine Learning workspace | +| `azurerm_machine_learning_workspace` | An Azure Machine Learning workspace instance | +| `azurerm_virtual_network` | An Azure Machine Learning workspace instance | +| `azurerm_subnet` | An Azure Machine Learning workspace instance | +| `azurerm_private_dns_zone` | Private DNS Zones for FQDNs required for Azure Machine Learning and associated resources | +| `azurerm_private_dns_zone_virtual_network_link` | Virtual network links of the Private DNS Zones to the virtual network resource | +| `azurerm_private_endpoint` | Private Endpoints for the Azure Machine Learning workspace and associated resources | +| `azurerm_machine_learning_compute_instance` | An Azure Machine Learning compute instance a single-node managed compute. | +| `azurerm_machine_learning_compute_cluster` | An Azure Machine Learning compute cluster as multi-node shared and managed compute. | +| `azurerm_network_security_group` | Network security group with required inbound and outbound rules for Azure Machine Learning. | + + +## Variables + +| Name | Description | +|-|-| +| name | Name of the deployment | +| environment | The deployment environment name (used for pre- and postfixing resource names) | +| location | The Azure region used for deployments | +| image_build_compute_name | Name of the compute cluster to be created and set to build docker images | +| training_subnet_resource_id | Resource ID of the existing training subnet | +| aks_subnet_resource_id | Resource ID of the existing aks subnet | +| ml_subnet_resource_id | Resource ID of the existing ML workspace subnet | +| privatelink_api_azureml_ms_resource_id | Resource ID of the existing privatelink.api.azureml.ms private dns zone | +| privatelink_azurecr_io_resource_id | Resource ID of the existing privatelink.azurecr.io private dns zone | +| privatelink_notebooks_azure_net_resource_id | Resource ID of the existing privatelink.notebooks.azure.net private dns zone | +| privatelink_blob_core_windows_net_resource_id | Resource ID of the existing privatelink.blob.core.windows.net private dns zone | +| privatelink_file_core_windows_net_resource_id | Resource ID of the existing privatelink.file.core.windows.net private dns zone | +| privatelink_vaultcore_azure_net_resource_id | Resource ID of the existing privatelink.vaultcore.azure.net private dns zone | + +## Usage + +```bash +terraform plan -var name=azureml567 -out demo.tfplan + +terraform apply "demo.tfplan" +``` + +## Learn more + +- If you are new to Azure Machine Learning, see [Azure Machine Learning service](https://azure.microsoft.com/services/machine-learning-service/) and [Azure Machine Learning documentation](https://docs.microsoft.com/azure/machine-learning/). +- To learn more about security configurations in Azure Machine Learning, see [Enterprise security and governance for Azure Machine Learning](https://docs.microsoft.com/en-us/azure/machine-learning/concept-enterprise-security). +- For all configurations of Azure Machine Learning in Terraform, see [Terraform Hashicorp AzureRM provider documentation](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/machine_learning_workspace). diff --git a/quickstart/202-machine-learning-moderately-secure-existing-VNet/variables.tf b/quickstart/202-machine-learning-moderately-secure-existing-VNet/variables.tf new file mode 100644 index 00000000..bd1ff762 --- /dev/null +++ b/quickstart/202-machine-learning-moderately-secure-existing-VNet/variables.tf @@ -0,0 +1,71 @@ +variable "name" { + type = string + description = "Name of the deployment" +} + +variable "environment" { + type = string + description = "Name of the environment" + default = "dev" +} + +variable "location" { + type = string + description = "Location of the resources" +} + +variable "image_build_compute_name" { + type = string + description = "Name of the compute cluster to be created and set to build docker images" + default = "image-builder" +} + +# Existing subnets variables + +variable "training_subnet_resource_id" { + type = string + description = "Resource ID of the existing training subnet" +} + +variable "aks_subnet_resource_id" { + type = string + description = "Resource ID of the existing aks subnet" +} + +variable "ml_subnet_resource_id" { + type = string + description = "Resource ID of the existing ML workspace subnet" +} + + +# Existing private DNS zones variables + +variable "privatelink_api_azureml_ms_resource_id" { + type = string + description = "Resource ID of the existing privatelink.api.azureml.ms private dns zone" +} + +variable "privatelink_azurecr_io_resource_id" { + type = string + description = "Resource ID of the existing privatelink.azurecr.io private dns zone" +} + +variable "privatelink_notebooks_azure_net_resource_id" { + type = string + description = "Resource ID of the existing privatelink.notebooks.azure.net private dns zone" +} + +variable "privatelink_blob_core_windows_net_resource_id" { + type = string + description = "Resource ID of the existing privatelink.blob.core.windows.net private dns zone" +} + +variable "privatelink_file_core_windows_net_resource_id" { + type = string + description = "Resource ID of the existing privatelink.file.core.windows.net private dns zone" +} + +variable "privatelink_vaultcore_azure_net_resource_id" { + type = string + description = "Resource ID of the existing privatelink.vaultcore.azure.net private dns zone" +} diff --git a/quickstart/202-machine-learning-moderately-secure-existing-VNet/workspace.tf b/quickstart/202-machine-learning-moderately-secure-existing-VNet/workspace.tf new file mode 100644 index 00000000..a443ce96 --- /dev/null +++ b/quickstart/202-machine-learning-moderately-secure-existing-VNet/workspace.tf @@ -0,0 +1,193 @@ +# Dependent resources for Azure Machine Learning +resource "azurerm_application_insights" "default" { + name = "appi-${var.name}-${var.environment}" + location = azurerm_resource_group.default.location + resource_group_name = azurerm_resource_group.default.name + application_type = "web" +} + +resource "azurerm_key_vault" "default" { + name = "kv-${var.name}-${var.environment}" + location = azurerm_resource_group.default.location + resource_group_name = azurerm_resource_group.default.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "premium" + purge_protection_enabled = true + + network_acls { + default_action = "Deny" + bypass = "AzureServices" + } +} + +resource "azurerm_storage_account" "default" { + name = "st${var.name}${var.environment}" + location = azurerm_resource_group.default.location + resource_group_name = azurerm_resource_group.default.name + account_tier = "Standard" + account_replication_type = "GRS" + + network_rules { + default_action = "Deny" + bypass = ["AzureServices"] + } +} + +resource "azurerm_container_registry" "default" { + name = "cr${var.name}${var.environment}" + location = azurerm_resource_group.default.location + resource_group_name = azurerm_resource_group.default.name + sku = "Premium" + admin_enabled = true + + network_rule_set { + default_action = "Deny" + } + public_network_access_enabled = false +} + +# Machine Learning workspace +resource "azurerm_machine_learning_workspace" "default" { + name = "mlw-${var.name}-${var.environment}" + location = azurerm_resource_group.default.location + resource_group_name = azurerm_resource_group.default.name + application_insights_id = azurerm_application_insights.default.id + key_vault_id = azurerm_key_vault.default.id + storage_account_id = azurerm_storage_account.default.id + container_registry_id = azurerm_container_registry.default.id + + identity { + type = "SystemAssigned" + } +} + +# Private endpoints +resource "azurerm_private_endpoint" "kv_ple" { + name = "ple-${var.name}-${var.environment}-kv" + location = azurerm_resource_group.default.location + resource_group_name = azurerm_resource_group.default.name + subnet_id = var.ml_subnet_resource_id + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [var.privatelink_vaultcore_azure_net_resource_id] + } + + private_service_connection { + name = "psc-${var.name}-kv" + private_connection_resource_id = azurerm_key_vault.default.id + subresource_names = [ "vault" ] + is_manual_connection = false + } +} + +resource "azurerm_private_endpoint" "st_ple_blob" { + name = "ple-${var.name}-${var.environment}-st-blob" + location = azurerm_resource_group.default.location + resource_group_name = azurerm_resource_group.default.name + subnet_id = var.ml_subnet_resource_id + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [var.privatelink_blob_core_windows_net_resource_id] + } + + private_service_connection { + name = "psc-${var.name}-st" + private_connection_resource_id = azurerm_storage_account.default.id + subresource_names = [ "blob" ] + is_manual_connection = false + } +} + +resource "azurerm_private_endpoint" "storage_ple_file" { + name = "ple-${var.name}-${var.environment}-st-file" + location = azurerm_resource_group.default.location + resource_group_name = azurerm_resource_group.default.name + subnet_id = var.ml_subnet_resource_id + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [var.privatelink_file_core_windows_net_resource_id] + } + + private_service_connection { + name = "psc-${var.name}-st" + private_connection_resource_id = azurerm_storage_account.default.id + subresource_names = [ "file" ] + is_manual_connection = false + } +} + +resource "azurerm_private_endpoint" "cr_ple" { + name = "ple-${var.name}-${var.environment}-cr" + location = azurerm_resource_group.default.location + resource_group_name = azurerm_resource_group.default.name + subnet_id = var.ml_subnet_resource_id + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [var.privatelink_azurecr_io_resource_id] + } + + private_service_connection { + name = "psc-${var.name}-cr" + private_connection_resource_id = azurerm_container_registry.default.id + subresource_names = [ "registry" ] + is_manual_connection = false + } +} + +resource "azurerm_private_endpoint" "mlw_ple" { + name = "ple-${var.name}-${var.environment}-mlw" + location = azurerm_resource_group.default.location + resource_group_name = azurerm_resource_group.default.name + subnet_id = var.ml_subnet_resource_id + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [ + var.privatelink_api_azureml_ms_resource_id, + var.privatelink_notebooks_azure_net_resource_id + ] + } + + private_service_connection { + name = "psc-${var.name}-mlw" + private_connection_resource_id = azurerm_machine_learning_workspace.default.id + subresource_names = [ "amlworkspace" ] + is_manual_connection = false + } +} + +# Compute cluster for image building required since the workspace is behind a vnet. +# For more details, see https://docs.microsoft.com/en-us/azure/machine-learning/tutorial-create-secure-workspace#configure-image-builds. +resource "azurerm_machine_learning_compute_cluster" "image-builder" { + name = "${var.image_build_compute_name}" + location = azurerm_resource_group.default.location + vm_priority = "LowPriority" + vm_size = "Standard_DS2_v2" + machine_learning_workspace_id = azurerm_machine_learning_workspace.default.id + subnet_resource_id = var.training_subnet_resource_id + + scale_settings { + min_node_count = 0 + max_node_count = 1 + scale_down_nodes_after_idle_duration = "PT15M" # 15 minutes + } + + identity { + type = "SystemAssigned" + } +} + +# Update workspace for image-build-compute +resource "null_resource" "ws_image_build_compute"{ + provisioner "local-exec" { + command = <