Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Azure AI Studio E2E Secure #333

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions quickstart/301-ai-studio-secure-e2e/data.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
data "azurerm_client_config" "current" {}
3 changes: 3 additions & 0 deletions quickstart/301-ai-studio-secure-e2e/locals.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
locals {
prefix = "${lower(var.prefix)}-${var.environment}"
}
148 changes: 148 additions & 0 deletions quickstart/301-ai-studio-secure-e2e/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
module "storage_account" {
source = "./modules/storage"

location = var.location
resource_group_name = azurerm_resource_group.resource_group.name
tags = var.tags
storage_account_name = replace("stg-${local.prefix}", "-", "")
storage_access_tier = "Hot"
storage_account_type = "StorageV2"
storage_account_tier = "Standard"
storage_account_replication_type = "LRS"
storage_blob_change_feed_enabled = false
storage_blob_container_delete_retention_in_days = 7
storage_blob_delete_retention_in_days = 7
storage_blob_cors_rules = {
azureml = { # Docs: https://learn.microsoft.com/en-us/azure/machine-learning/prompt-flow/troubleshoot-guidance?view=azureml-api-2#flow-is-missing
allowed_headers = ["*"]
allowed_methods = ["GET", "HEAD", "PUT", "DELETE", "OPTIONS", "POST", "PATCH"]
allowed_origins = ["https://mlworkspace.azure.ai", "https://ml.azure.com", "https://*.ml.azure.com", "https://ai.azure.com", "https://*.ai.azure.com", "https://mlworkspacecanary.azure.ai", "https://mlworkspace.azureml-test.net"]
exposed_headers = ["*"]
max_age_in_seconds = 1800
}
}
storage_blob_last_access_time_enabled = false
storage_blob_versioning_enabled = false
storage_is_hns_enabled = false
storage_network_bypass = ["AzureServices"]
storage_network_private_link_access = [
"/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/*/providers/Microsoft.MachineLearningServices/workspaces/*",
]
storage_public_network_access_enabled = true
storage_nfsv3_enabled = false
storage_sftp_enabled = false
storage_shared_access_key_enabled = true
storage_container_names = []
storage_static_website = []
diagnostics_configurations = []
subnet_id = var.subnet_id
private_endpoint_subresource_names = ["blob", "file", "queue", "table"]
private_dns_zone_id_blob = var.private_dns_zone_id_blob
private_dns_zone_id_file = var.private_dns_zone_id_file
private_dns_zone_id_table = var.private_dns_zone_id_table
private_dns_zone_id_queue = var.private_dns_zone_id_queue
private_dns_zone_id_web = ""
private_dns_zone_id_dfs = ""
customer_managed_key = var.customer_managed_key
}

module "key_vault" {
source = "./modules/keyvault"

location = var.location
resource_group_name = azurerm_resource_group.resource_group.name
tags = var.tags
key_vault_name = "kv-${local.prefix}"
key_vault_sku_name = "standard"
key_vault_soft_delete_retention_days = 7
diagnostics_configurations = []
subnet_id = var.subnet_id
private_dns_zone_id_vault = var.private_dns_zone_id_vault
}

module "log_analytics_workspace" {
source = "./modules/loganalytics"

location = var.location
resource_group_name = azurerm_resource_group.resource_group.name
tags = var.tags
log_analytics_workspace_name = "log-${local.prefix}"
log_analytics_workspace_retention_in_days = 30
diagnostics_configurations = []
}

module "application_insights" {
source = "./modules/applicationinsights"

location = var.location
resource_group_name = azurerm_resource_group.resource_group.name
tags = var.tags
application_insights_name = "ai-${local.prefix}"
application_insights_application_type = "web"
application_insights_log_analytics_workspace_id = module.log_analytics_workspace.log_analytics_workspace_id
diagnostics_configurations = []
}

module "container_registry" {
source = "./modules/containerregistry"

location = var.location
resource_group_name = azurerm_resource_group.resource_group.name
tags = var.tags
container_registry_name = "acr-${local.prefix}"
container_registry_admin_enabled = false
container_registry_anonymous_pull_enabled = false
container_registry_data_endpoint_enabled = false
container_registry_export_policy_enabled = false
container_registry_quarantine_policy_enabled = false
container_registry_retention_policy_in_days = 7
container_registry_trust_policy_enabled = false
container_registry_zone_redundancy_enabled = false
diagnostics_configurations = []
subnet_id = var.subnet_id
private_dns_zone_id_container_registry = var.private_dns_zone_id_container_registry
customer_managed_key = var.customer_managed_key
}

module "ai_studio_hub" {
source = "./modules/aistudiohub"

location = var.location
resource_group_name = azurerm_resource_group.resource_group.name
tags = var.tags
ai_studio_hub_name = "aih-${local.prefix}"
application_insights_id = module.application_insights.application_insights_id
container_registry_id = module.container_registry.container_registry_id
key_vault_id = module.key_vault.key_vault_id
storage_account_id = module.storage_account.storage_account_id
ai_studio_hub_provision_managed_network = false
ai_studio_hub_connections = {}
diagnostics_configurations = []
subnet_id = var.subnet_id
private_dns_zone_id_machine_learning_api = var.private_dns_zone_id_machine_learning_api
private_dns_zone_id_machine_learning_notebooks = var.private_dns_zone_id_machine_learning_notebooks
customer_managed_key = var.customer_managed_key
}

module "ai_studio_project" {
source = "./modules/aistudioproject"

location = var.location
resource_group_name = azurerm_resource_group.resource_group.name
tags = var.tags
ai_studio_project_name = "aip-${local.prefix}"
ai_studio_hub_id = module.ai_studio_hub.ai_studio_hub_id
ai_studio_project_connections = {}
}

module "ai_studio_outbound_connection_rules" {
source = "./modules/aistudiooutboundrules"

ai_studio_hub_id = module.ai_studio_hub.ai_studio_hub_id
ai_studio_hub_storage_account_id = module.storage_account.storage_account_id
ai_studio_hub_provision_managed_network = false
ai_studio_hub_approve_private_endpoints = false
ai_studio_hub_outbound_rules_fqdns = {}
ai_studio_hub_outbound_rules_service_endpoints = {}
ai_studio_hub_outbound_rules_private_endpoints = {}
}
203 changes: 203 additions & 0 deletions quickstart/301-ai-studio-secure-e2e/modules/aistudiohub/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
<!-- BEGIN_TF_DOCS -->
# Azure AI Studio Hub Terraform Module

## Info for CMK Deployments

CMK deployments are currently not supported because the service-side encryption of metadata is not supported for hub workspaces today. Once this is supported, we can update the module and enable CMK deployments. More details: https://learn.microsoft.com/en-us/azure/machine-learning/concept-customer-managed-keys?view=azureml-api-2#preview-service-side-encryption-of-metadata

The standard deployment is currently blocked because of a managed Cosmos DB that gets created inside the customer environment. The workspace requires the asignment of a contributor role on that managed resource group in which the cosmos DB will reside. This assignment is currently being blocked by Azure Policies.

## Info for user-defined outbound rule management

We have decided to manage the outbund rules using a separate Terraform module. Reasons for this decision and the module can be found here: [Azure AI Studio Outbound Rules](/modules/aistudiooutboundrules/)

## Documentation
<!-- markdownlint-disable MD033 -->

## Requirements

The following requirements are needed by this module:

- <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) (>=0.12)

- <a name="requirement_azapi"></a> [azapi](#requirement\_azapi) (>= 1.14.0)

- <a name="requirement_azurerm"></a> [azurerm](#requirement\_azurerm) (>= 3.100.0)

- <a name="requirement_time"></a> [time](#requirement\_time) (>= 0.9.1)

## Modules

No modules.

<!-- markdownlint-disable MD013 -->
<!-- markdownlint-disable MD034 -->
## Required Inputs

The following input variables are required:

### <a name="input_ai_studio_hub_name"></a> [ai\_studio\_hub\_name](#input\_ai\_studio\_hub\_name)

Description: Specifies the name of the ai studio hub.

Type: `string`

### <a name="input_application_insights_id"></a> [application\_insights\_id](#input\_application\_insights\_id)

Description: Specifies the id of application insights that will be connected to the ai studio hub.

Type: `string`

### <a name="input_container_registry_id"></a> [container\_registry\_id](#input\_container\_registry\_id)

Description: Specifies the id of the container registry that will be connected to the ai studio hub.

Type: `string`

### <a name="input_key_vault_id"></a> [key\_vault\_id](#input\_key\_vault\_id)

Description: Specifies the id of the key vaul that will be connected to the ai studio hub.

Type: `string`

### <a name="input_location"></a> [location](#input\_location)

Description: Specifies the location of all resources.

Type: `string`

### <a name="input_resource_group_name"></a> [resource\_group\_name](#input\_resource\_group\_name)

Description: Specifies the resource group name in which all resources will get deployed.

Type: `string`

### <a name="input_storage_account_id"></a> [storage\_account\_id](#input\_storage\_account\_id)

Description: Specifies the id of the storage account that will be connected to the ai studio hub.

Type: `string`

### <a name="input_subnet_id"></a> [subnet\_id](#input\_subnet\_id)

Description: Specifies the resource id of a subnet in which the private endpoints get created.

Type: `string`

## Optional Inputs

The following input variables are optional (have default values):

### <a name="input_ai_studio_hub_connections"></a> [ai\_studio\_hub\_connections](#input\_ai\_studio\_hub\_connections)

Description: Specifies the connections that should be added to the AI Studio Hub. Only provide connections to be shared with all projects at the hub level.

Type:

```hcl
map(object({
auth_type = optional(string, "AAD")
category = string
credentials = optional(any, null)
target = string
metadata = any
}))
```

Default: `{}`

### <a name="input_ai_studio_hub_provision_managed_network"></a> [ai\_studio\_hub\_provision\_managed\_network](#input\_ai\_studio\_hub\_provision\_managed\_network)

Description: Specifies whether the managed vnet should be providioned as part of the ai studio hub deployment.

Type: `bool`

Default: `false`

### <a name="input_connectivity_delay_in_seconds"></a> [connectivity\_delay\_in\_seconds](#input\_connectivity\_delay\_in\_seconds)

Description: Specifies the delay in seconds after the private endpoint deployment (required for the DNS automation via Policies).

Type: `number`

Default: `120`

### <a name="input_customer_managed_key"></a> [customer\_managed\_key](#input\_customer\_managed\_key)

Description: Specifies the customer managed key configurations.

Type:

```hcl
object({
key_vault_id = string,
key_vault_key_versionless_id = string,
user_assigned_identity_id = string,
user_assigned_identity_client_id = string,
})
```

Default: `null`

### <a name="input_diagnostics_configurations"></a> [diagnostics\_configurations](#input\_diagnostics\_configurations)

Description: Specifies the diagnostic configuration for the service.

Type:

```hcl
list(object({
log_analytics_workspace_id = optional(string, ""),
storage_account_id = optional(string, "")
}))
```

Default: `[]`

### <a name="input_private_dns_zone_id_machine_learning_api"></a> [private\_dns\_zone\_id\_machine\_learning\_api](#input\_private\_dns\_zone\_id\_machine\_learning\_api)

Description: Specifies the resource ID of the private DNS zone for the Purview account. Not required if DNS A-records get created via Azure Policy.

Type: `string`

Default: `""`

### <a name="input_private_dns_zone_id_machine_learning_notebooks"></a> [private\_dns\_zone\_id\_machine\_learning\_notebooks](#input\_private\_dns\_zone\_id\_machine\_learning\_notebooks)

Description: Specifies the resource ID of the private DNS zone for the Purview account. Not required if DNS A-records get created via Azure Policy.

Type: `string`

Default: `""`

### <a name="input_tags"></a> [tags](#input\_tags)

Description: Specifies a key value map of tags to set on every taggable resources.

Type: `map(string)`

Default: `{}`

## Outputs

The following outputs are exported:

### <a name="output_ai_studio_hub_id"></a> [ai\_studio\_hub\_id](#output\_ai\_studio\_hub\_id)

Description: Specifies the resource id of the ai studio hub.

### <a name="output_ai_studio_hub_name"></a> [ai\_studio\_hub\_name](#output\_ai\_studio\_hub\_name)

Description: Specifies the name of the ai studio hub.

### <a name="output_ai_studio_hub_principal_id"></a> [ai\_studio\_hub\_principal\_id](#output\_ai\_studio\_hub\_principal\_id)

Description: Specifies the principal id of the ai studio hub.

### <a name="output_ai_studio_hub_setup_completed"></a> [ai\_studio\_hub\_setup\_completed](#output\_ai\_studio\_hub\_setup\_completed)

Description: Specifies whether the connectivity and identity has been successfully configured.

<!-- markdownlint-enable -->

<!-- END_TF_DOCS -->
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Azure AI Studio Hub Terraform Module

## Info for CMK Deployments

CMK deployments are currently not supported because the service-side encryption of metadata is not supported for hub workspaces today. Once this is supported, we can update the module and enable CMK deployments. More details: https://learn.microsoft.com/en-us/azure/machine-learning/concept-customer-managed-keys?view=azureml-api-2#preview-service-side-encryption-of-metadata

The standard deployment is currently blocked because of a managed Cosmos DB that gets created inside the customer environment. The workspace requires the asignment of a contributor role on that managed resource group in which the cosmos DB will reside. This assignment is currently being blocked by Azure Policies.

## Info for user-defined outbound rule management

We have decided to manage the outbund rules using a separate Terraform module. Reasons for this decision and the module can be found here: [Azure AI Studio Outbound Rules](/modules/aistudiooutboundrules/)
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
resource "azapi_resource" "ai_studio_hub_connections" {
for_each = var.ai_studio_hub_connections

type = "Microsoft.MachineLearningServices/workspaces/connections@2024-04-01"
name = each.key
parent_id = azapi_resource.ai_studio_hub.id

body = {
properties = {
authType = each.value.auth_type
category = each.value.category
credentials = each.value.credentials
isSharedToAll = true
sharedUserList = []
target = each.value.target
metadata = each.value.metadata
}
}

response_export_values = []
schema_validation_enabled = false # Can be reverted once this is closed: https://github.com/Azure/terraform-provider-azapi/issues/524
locks = []
ignore_casing = false
ignore_missing_property = true
}
Loading
Loading