From 497beb193bdb5875cd142428f3f1775e0325d223 Mon Sep 17 00:00:00 2001 From: Arnaud Lheureux Date: Mon, 16 Jun 2025 19:41:29 +0800 Subject: [PATCH 1/6] Update provider versions and resource types for dev center modules --- modules/dev_center/README.md | 4 ++-- modules/dev_center/module.tf | 4 ++-- modules/dev_center_catalog/README.md | 4 ++-- modules/dev_center_catalog/module.tf | 4 ++-- modules/dev_center_environment_type/README.md | 4 ++-- modules/dev_center_environment_type/module.tf | 4 ++-- modules/dev_center_project/README.md | 4 ++-- modules/dev_center_project/module.tf | 4 ++-- modules/resource_group/README.md | 4 ++-- modules/resource_group/module.tf | 2 +- provider.tf | 11 +++++++++-- 11 files changed, 28 insertions(+), 21 deletions(-) diff --git a/modules/dev_center/README.md b/modules/dev_center/README.md index c76be0f..86f0031 100644 --- a/modules/dev_center/README.md +++ b/modules/dev_center/README.md @@ -92,14 +92,14 @@ For more examples including all possible configurations, see the [Dev Center exa |------|---------| | [terraform](#requirement\_terraform) | >= 1.9.0 | | [azapi](#requirement\_azapi) | ~> 2.4.0 | -| [azurecaf](#requirement\_azurecaf) | ~> 1.2.0 | +| [azurecaf](#requirement\_azurecaf) | ~> 1.2.29 | ## Providers | Name | Version | |------|---------| | [azapi](#provider\_azapi) | 2.4.0 | -| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurecaf](#provider\_azurecaf) | 1.2.29 | ## Modules diff --git a/modules/dev_center/module.tf b/modules/dev_center/module.tf index fed1abe..c6bedd4 100644 --- a/modules/dev_center/module.tf +++ b/modules/dev_center/module.tf @@ -3,7 +3,7 @@ terraform { required_providers { azurecaf = { source = "aztfmod/azurecaf" - version = "~> 1.2.0" + version = "~> 1.2.29" } azapi = { source = "Azure/azapi" @@ -25,7 +25,7 @@ locals { # Using resource instead of data source to ensure stable naming across plan/apply resource "azurecaf_name" "dev_center" { name = var.dev_center.name - resource_type = "general" + resource_type = "azurerm_dev_center" prefixes = var.global_settings.prefixes random_length = var.global_settings.random_length clean_input = true diff --git a/modules/dev_center_catalog/README.md b/modules/dev_center_catalog/README.md index ae91977..a4bb6c4 100644 --- a/modules/dev_center_catalog/README.md +++ b/modules/dev_center_catalog/README.md @@ -91,14 +91,14 @@ This module implements the [Microsoft.DevCenter/devcenters/catalogs](https://lea |------|---------| | [terraform](#requirement\_terraform) | >= 1.9.0 | | [azapi](#requirement\_azapi) | ~> 2.4.0 | -| [azurecaf](#requirement\_azurecaf) | ~> 1.2.0 | +| [azurecaf](#requirement\_azurecaf) | ~> 1.2.29 | ## Providers | Name | Version | |------|---------| | [azapi](#provider\_azapi) | 2.4.0 | -| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurecaf](#provider\_azurecaf) | 1.2.29 | ## Modules diff --git a/modules/dev_center_catalog/module.tf b/modules/dev_center_catalog/module.tf index fd1e839..88eebbb 100644 --- a/modules/dev_center_catalog/module.tf +++ b/modules/dev_center_catalog/module.tf @@ -3,7 +3,7 @@ terraform { required_providers { azurecaf = { source = "aztfmod/azurecaf" - version = "~> 1.2.0" + version = "~> 1.2.29" } azapi = { source = "Azure/azapi" @@ -21,7 +21,7 @@ locals { resource "azurecaf_name" "catalog" { name = var.catalog.name - resource_type = "general" + resource_type = "azurerm_dev_center_catalog" prefixes = var.global_settings.prefixes random_length = var.global_settings.random_length clean_input = true diff --git a/modules/dev_center_environment_type/README.md b/modules/dev_center_environment_type/README.md index 36ce944..0b3b94e 100644 --- a/modules/dev_center_environment_type/README.md +++ b/modules/dev_center_environment_type/README.md @@ -106,14 +106,14 @@ For more examples, see the [environment type examples](../../../examples/dev_cen |------|---------| | [terraform](#requirement\_terraform) | >= 1.9.0 | | [azapi](#requirement\_azapi) | ~> 2.4.0 | -| [azurecaf](#requirement\_azurecaf) | ~> 1.2.0 | +| [azurecaf](#requirement\_azurecaf) | ~> 1.2.29 | ## Providers | Name | Version | |------|---------| | [azapi](#provider\_azapi) | 2.4.0 | -| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurecaf](#provider\_azurecaf) | 1.2.29 | ## Modules diff --git a/modules/dev_center_environment_type/module.tf b/modules/dev_center_environment_type/module.tf index e1dacd2..524362e 100644 --- a/modules/dev_center_environment_type/module.tf +++ b/modules/dev_center_environment_type/module.tf @@ -3,7 +3,7 @@ terraform { required_providers { azurecaf = { source = "aztfmod/azurecaf" - version = "~> 1.2.0" + version = "~> 1.2.29" } azapi = { source = "Azure/azapi" @@ -22,7 +22,7 @@ locals { resource "azurecaf_name" "environment_type" { name = var.environment_type.name - resource_type = "general" + resource_type = "azurerm_dev_center_environment_type" prefixes = var.global_settings.prefixes random_length = var.global_settings.random_length clean_input = true diff --git a/modules/dev_center_project/README.md b/modules/dev_center_project/README.md index 0f359fe..3bdc9d0 100644 --- a/modules/dev_center_project/README.md +++ b/modules/dev_center_project/README.md @@ -111,14 +111,14 @@ For more examples, see the [Dev Center Project examples](../../../examples/dev_c |------|---------| | [terraform](#requirement\_terraform) | >= 1.9.0 | | [azapi](#requirement\_azapi) | ~> 2.4.0 | -| [azurecaf](#requirement\_azurecaf) | ~> 1.2.0 | +| [azurecaf](#requirement\_azurecaf) | ~> 1.2.29 | ## Providers | Name | Version | |------|---------| | [azapi](#provider\_azapi) | 2.4.0 | -| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurecaf](#provider\_azurecaf) | 1.2.29 | ## Modules diff --git a/modules/dev_center_project/module.tf b/modules/dev_center_project/module.tf index 868997c..b25866f 100644 --- a/modules/dev_center_project/module.tf +++ b/modules/dev_center_project/module.tf @@ -3,7 +3,7 @@ terraform { required_providers { azurecaf = { source = "aztfmod/azurecaf" - version = "~> 1.2.0" + version = "~> 1.2.29" } azapi = { source = "Azure/azapi" @@ -19,7 +19,7 @@ locals { resource "azurecaf_name" "project" { name = var.project.name - resource_type = "general" + resource_type = "azurerm_dev_center_project" prefixes = var.global_settings.prefixes random_length = var.global_settings.random_length clean_input = true diff --git a/modules/resource_group/README.md b/modules/resource_group/README.md index e7fcab0..b59a4de 100644 --- a/modules/resource_group/README.md +++ b/modules/resource_group/README.md @@ -84,14 +84,14 @@ For more examples, see the [Resource Group examples](../../../examples/resource_ |------|---------| | [terraform](#requirement\_terraform) | >= 1.9.0 | | [azapi](#requirement\_azapi) | ~> 2.4.0 | -| [azurecaf](#requirement\_azurecaf) | ~> 1.2.0 | +| [azurecaf](#requirement\_azurecaf) | ~> 1.2.29 | ## Providers | Name | Version | |------|---------| | [azapi](#provider\_azapi) | 2.4.0 | -| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurecaf](#provider\_azurecaf) | 1.2.29 | ## Modules diff --git a/modules/resource_group/module.tf b/modules/resource_group/module.tf index 56297a3..ff605fa 100644 --- a/modules/resource_group/module.tf +++ b/modules/resource_group/module.tf @@ -3,7 +3,7 @@ terraform { required_providers { azurecaf = { source = "aztfmod/azurecaf" - version = "~> 1.2.0" + version = "~> 1.2.29" } azapi = { source = "Azure/azapi" diff --git a/provider.tf b/provider.tf index afbf7bf..c93e40a 100644 --- a/provider.tf +++ b/provider.tf @@ -1,14 +1,21 @@ terraform { required_providers { - azapi = { source = "Azure/azapi" version = "~> 2.4.0" } azurecaf = { source = "aztfmod/azurecaf" - version = "~> 1.2.0" + version = "~> 1.2.29" + } + azurerm = { + source = "hashicorp/azurerm" + version = "~> 4.26.0" } } required_version = ">= 1.12.1" } + +provider "azurerm" { + features {} +} From 80bb80d09a63be046ad7475e47cd2abf01cf3931 Mon Sep 17 00:00:00 2001 From: Arnaud Lheureux Date: Tue, 17 Jun 2025 11:48:19 +0800 Subject: [PATCH 2/6] Add lifecycle block to ignore changes to system-managed tags in DevCenter resources --- modules/dev_center/module.tf | 7 +++++++ modules/dev_center_project/module.tf | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/modules/dev_center/module.tf b/modules/dev_center/module.tf index fed1abe..ab4e4d8 100644 --- a/modules/dev_center/module.tf +++ b/modules/dev_center/module.tf @@ -77,6 +77,13 @@ resource "azapi_resource" "dev_center" { tags = local.tags response_export_values = ["properties"] + + # Ignore changes to system-managed tags that Azure automatically adds + lifecycle { + ignore_changes = [ + tags["hidden-title"] + ] + } } data "azapi_client_config" "current" {} \ No newline at end of file diff --git a/modules/dev_center_project/module.tf b/modules/dev_center_project/module.tf index 868997c..4950bad 100644 --- a/modules/dev_center_project/module.tf +++ b/modules/dev_center_project/module.tf @@ -91,4 +91,11 @@ resource "azapi_resource" "project" { } : null } } + + # Ignore changes to system-managed tags that Azure automatically adds + lifecycle { + ignore_changes = [ + tags["hidden-title"] + ] + } } From 9c7004e1dd192cd945377668b7daa9febf0f27df Mon Sep 17 00:00:00 2001 From: Arnaud Lheureux Date: Tue, 17 Jun 2025 13:22:03 +0800 Subject: [PATCH 3/6] feat: Update Dev Center Dev Box Definition module - Upgrade azurecaf provider version to 1.2.29. - Enhance SKU configuration to support both simple name and full object. - Introduce os_storage_type variable for OS disk configuration. - Simplify hibernate_support to a boolean value. - Enable schema validation for azapi resource. - Add new outputs for image validation status and details. - Create comprehensive unit tests for Dev Box Definition module. - Update README with test coverage and instructions. --- .../enhanced_case/configuration.tfvars | 69 +- .../simple_case/configuration.tfvars | 4 +- modules/dev_center/README.md | 2 +- .../dev_center_dev_box_definition/README.md | 614 +++++++++++++++++- .../dev_center_dev_box_definition/module.tf | 38 +- .../dev_center_dev_box_definition/output.tf | 25 + .../variables.tf | 61 +- modules/dev_center_project/README.md | 2 +- tests/run_tests.sh | 2 +- .../dev_center_dev_box_definition/README.md | 82 +++ .../devbox_definition_test.tftest.hcl | 286 +++++++- variables.tf | 18 +- 12 files changed, 1114 insertions(+), 89 deletions(-) create mode 100644 tests/unit/dev_center_dev_box_definition/README.md diff --git a/examples/dev_center_dev_box_definition/enhanced_case/configuration.tfvars b/examples/dev_center_dev_box_definition/enhanced_case/configuration.tfvars index 8fd77f9..ddb9831 100644 --- a/examples/dev_center_dev_box_definition/enhanced_case/configuration.tfvars +++ b/examples/dev_center_dev_box_definition/enhanced_case/configuration.tfvars @@ -70,9 +70,7 @@ dev_center_dev_box_definitions = { # Standard Windows 11 development environment image_reference_id = "galleries/default/images/microsoftvisualstudio_visualstudioplustools_vs-2022-ent-general-win11-m365-gen2" sku_name = "general_i_8c32gb256ssd_v2" - hibernate_support = { - enabled = true # Enable hibernate for cost optimization - } + hibernate_support = true # Enable hibernate for cost optimization tags = { module = "dev_center_dev_box_definition" @@ -96,9 +94,10 @@ dev_center_dev_box_definitions = { # Standard Windows 11 development environment } sku_name = "general_i_32c128gb1024ssd_v2" # High-performance SKU - hibernate_support = { - enabled = false # Keep running for long-running AI training - } + hibernate_support = false # Keep running for long-running AI training + + # Test osStorageType property + os_storage_type = "ssd_1024gb" tags = { module = "dev_center_dev_box_definition" @@ -110,53 +109,65 @@ dev_center_dev_box_definitions = { # Standard Windows 11 development environment auto_delete = "disabled" } } - # Linux development environment - ubuntu_development = { - name = "ubuntu-development" + # Specialized environment for data science work + win11_data_science = { + name = "win11-datascience" dev_center = { key = "platform" } resource_group = { key = "rg_devbox" - } # Ubuntu development image (using built-in Ubuntu 22.04) - image_reference_id = "galleries/default/images/canonical_0001-com-ubuntu-server-jammy_22_04-lts-gen2" - sku_name = "general_i_16c64gb512ssd_v2" + } # Data science image (using built-in Windows 11 with development tools) + image_reference_id = "galleries/default/images/microsoftvisualstudio_visualstudioplustools_vs-2022-ent-general-win11-m365-gen2" - hibernate_support = { - enabled = true - } + # Use simple SKU configuration + sku_name = "general_i_16c64gb512ssd_v2" + + hibernate_support = true + + # Test osStorageType property + os_storage_type = "ssd_512gb" tags = { module = "dev_center_dev_box_definition" - image_type = "ubuntu" - tier = "standard" - purpose = "linux-development" + image_type = "win11" + tier = "specialized" + purpose = "data-science" + tools = "python-r-jupyter" auto_delete = "enabled" } } - # Specialized environment for data science work - win11_data_science = { - name = "win11-datascience" + # Advanced SKU object configuration example + win11_enterprise_advanced = { + name = "win11-enterprise-advanced" dev_center = { key = "platform" } resource_group = { key = "rg_devbox" - } # Data science image (using built-in Windows 11 with development tools) - image_reference_id = "galleries/default/images/microsoftvisualstudio_visualstudioplustools_vs-2022-ent-general-win11-m365-gen2" - sku_name = "general_i_16c64gb512ssd_v2" + } + # Enterprise development image + image_reference_id = "galleries/default/images/microsoftvisualstudio_visualstudioplustools_vs-2022-pro-general-win11-m365-gen2" - hibernate_support = { - enabled = true + # Advanced SKU object configuration instead of simple sku_name + sku = { + name = "general_i_16c64gb512ssd_v2" + tier = "Standard" } + hibernate_support = true + + # Test osStorageType property + os_storage_type = "ssd_512gb" + tags = { module = "dev_center_dev_box_definition" image_type = "win11" - tier = "specialized" - purpose = "data-science" - tools = "python-r-jupyter" + tier = "enterprise" + purpose = "enterprise-development" + sku_type = "advanced_object" auto_delete = "enabled" + config_type = "advanced" } } } diff --git a/examples/dev_center_dev_box_definition/simple_case/configuration.tfvars b/examples/dev_center_dev_box_definition/simple_case/configuration.tfvars index 7a8e33b..e80696e 100644 --- a/examples/dev_center_dev_box_definition/simple_case/configuration.tfvars +++ b/examples/dev_center_dev_box_definition/simple_case/configuration.tfvars @@ -41,9 +41,7 @@ dev_center_dev_box_definitions = { image_reference_id = "galleries/default/images/microsoftvisualstudio_visualstudioplustools_vs-2022-ent-general-win11-m365-gen2" sku_name = "general_i_8c32gb256ssd_v2" - hibernate_support = { - enabled = false - } + hibernate_support = false tags = { module = "dev_center_dev_box_definition" diff --git a/modules/dev_center/README.md b/modules/dev_center/README.md index e667ff3..437cfee 100644 --- a/modules/dev_center/README.md +++ b/modules/dev_center/README.md @@ -99,7 +99,7 @@ For more examples including all possible configurations, see the [Dev Center exa | Name | Version | |------|---------| | [azapi](#provider\_azapi) | 2.4.0 | -| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurecaf](#provider\_azurecaf) | 1.2.29 | ## Modules diff --git a/modules/dev_center_dev_box_definition/README.md b/modules/dev_center_dev_box_definition/README.md index 885c3a2..ccaae8b 100644 --- a/modules/dev_center_dev_box_definition/README.md +++ b/modules/dev_center_dev_box_definition/README.md @@ -6,16 +6,273 @@ This module creates an Azure DevCenter DevBox Definition using the AzAPI provide The DevBox Definition module enables the creation and management of DevBox definitions within Azure DevCenter. It leverages the AzAPI provider to ensure compatibility with the latest Azure features and APIs, following DevFactory standardization patterns. +## Quick Reference + +### ๐Ÿš€ **Most Common Configurations** + +```hcl +# Windows 11 + Visual Studio 2022 Enterprise (Recommended for .NET development) +image_reference_id = "galleries/default/images/microsoftvisualstudio_visualstudioplustools_vs-2022-ent-general-win11-m365-gen2" +sku_name = "general_i_16c64gb512ssd_v2" # 16 vCPU, 64 GB RAM, 512 GB SSD + +# Windows 11 Enterprise with Microsoft 365 Apps (Recommended for general development) +image_reference_id = "galleries/default/images/microsoftwindowsdesktop_windows-ent-cpc_win11-24h2-ent-cpc-m365" +sku_name = "general_i_8c32gb256ssd_v2" # 8 vCPU, 32 GB RAM, 256 GB SSD + +# High-performance development (AI/ML, large projects) +image_reference_id = "galleries/default/images/microsoftvisualstudio_visualstudioplustools_vs-2022-ent-general-win11-m365-gen2" +sku_name = "general_i_32c128gb1024ssd_v2" # 32 vCPU, 128 GB RAM, 1024 GB SSD +``` + +### ๐Ÿ”ง **Essential Discovery Commands** +```bash +# List available DevBox SKUs (subscription-wide) +az devcenter admin sku list --output table + +# List available images (requires DevCenter context) +az devcenter admin image list --dev-center-name "mydevcenter" --resource-group "myrg" --gallery-name "default" --query "[].name" -o table + +# List VM SKUs by location (alternative for region-specific sizing) +az vm list-skus --location "East US" --resource-type "virtualMachines" --query "[?contains(name, 'Standard_D')]" --output table + +# Validate your configuration +terraform plan -var-file="configuration.tfvars" +``` + ## Features - Uses AzAPI provider v2.4.0 for latest Azure features - Implements latest Azure DevCenter API (2025-04-01-preview) - Supports multiple image reference formats -- Configurable SKU and storage options +- Configurable SKU and storage options with full type safety - Hibernate support configuration - Integrates with azurecaf naming conventions - Manages resource tags (global + specific) -- Provides strong input validation +- Provides strong input validation and type checking +- Schema-aligned object types for Azure API compatibility + +## Finding DevBox Configuration Options + +### ๐Ÿ–ผ๏ธ **Finding Available Images** + +DevBox definitions support several image sources: + +#### **Built-in Gallery Images (Recommended)** +Use the relative path format for Microsoft's built-in images: + +```hcl +# Windows 11 with Visual Studio 2022 Enterprise +image_reference_id = "galleries/default/images/microsoftvisualstudio_visualstudioplustools_vs-2022-ent-general-win11-m365-gen2" + +# Windows 10 with Visual Studio 2022 Professional +image_reference_id = "galleries/default/images/microsoftvisualstudio_visualstudioplustools_vs-2022-pro-general-win10-m365-gen2" + +# Windows 11 Enterprise with Microsoft 365 Apps +image_reference_id = "galleries/default/images/microsoftwindowsdesktop_windows-ent-cpc_win11-24h2-ent-cpc-m365" +``` + +#### **Complete List of Available Built-in Images** +```hcl +# Visual Studio Images +"galleries/default/images/microsoftvisualstudio_visualstudioplustools_vs-2022-ent-general-win11-m365-gen2" # VS 2022 Enterprise + Win11 + M365 +"galleries/default/images/microsoftvisualstudio_visualstudioplustools_vs-2022-pro-general-win11-m365-gen2" # VS 2022 Professional + Win11 + M365 +"galleries/default/images/microsoftvisualstudio_visualstudioplustools_vs-2022-ent-general-win10-m365-gen2" # VS 2022 Enterprise + Win10 + M365 +"galleries/default/images/microsoftvisualstudio_visualstudioplustools_vs-2022-pro-general-win10-m365-gen2" # VS 2022 Professional + Win10 + M365 +"galleries/default/images/microsoftvisualstudio_visualstudio2019plustools_vs-2019-ent-general-win11-m365-gen2" # VS 2019 Enterprise + Win11 + M365 +"galleries/default/images/microsoftvisualstudio_visualstudio2019plustools_vs-2019-pro-general-win11-m365-gen2" # VS 2019 Professional + Win11 + M365 +"galleries/default/images/microsoftvisualstudio_visualstudio2019plustools_vs-2019-ent-general-win10-m365-gen2" # VS 2019 Enterprise + Win10 + M365 +"galleries/default/images/microsoftvisualstudio_visualstudio2019plustools_vs-2019-pro-general-win10-m365-gen2" # VS 2019 Professional + Win10 + M365 + +# Windows Base Images +"galleries/default/images/microsoftwindowsdesktop_windows-ent-cpc_win11-24h2-ent-cpc-m365" # Windows 11 Enterprise 24H2 + M365 +"galleries/default/images/microsoftwindowsdesktop_windows-ent-cpc_win11-24h2-ent-cpc" # Windows 11 Enterprise 24H2 +"galleries/default/images/microsoftwindowsdesktop_windows-ent-cpc_win11-23h2-ent-cpc-m365" # Windows 11 Enterprise 23H2 + M365 +"galleries/default/images/microsoftwindowsdesktop_windows-ent-cpc_win11-23h2-ent-cpc" # Windows 11 Enterprise 23H2 +"galleries/default/images/microsoftwindowsdesktop_windows-ent-cpc_win11-22h2-ent-cpc-m365" # Windows 11 Enterprise 22H2 + M365 +"galleries/default/images/microsoftwindowsdesktop_windows-ent-cpc_win11-22h2-ent-cpc" # Windows 11 Enterprise 22H2 +"galleries/default/images/microsoftwindowsdesktop_windows-ent-cpc_win11-22h2-ent-cpc-os" # Windows 11 Enterprise 22H2 + OS Optimizations +"galleries/default/images/microsoftwindowsdesktop_windows-ent-cpc_win10-22h2-ent-cpc-m365" # Windows 10 Enterprise 22H2 + M365 +"galleries/default/images/microsoftwindowsdesktop_windows-ent-cpc_win10-22h2-ent-cpc" # Windows 10 Enterprise 22H2 + +# Developer-Optimized Images +"galleries/default/images/microsoftvisualstudio_windowsplustools_base-win11-gen2" # Windows 11 + Developer Optimizations 24H2 +``` + +#### **List Available Built-in Images** +```bash +# List all available gallery images +az devcenter admin gallery list --dev-center-name "mydevcenter" --resource-group "myrg" + +# Get image details +az devcenter admin image list --dev-center-name "mydevcenter" --resource-group "myrg" --gallery-name "default" +``` + +#### **Custom Gallery Images** +```hcl +# Full resource ID format +image_reference_id = "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/myrg/providers/Microsoft.Compute/galleries/mygallery/images/myimage/versions/1.0.0" + +# Or use the object format +image_reference = { + id = "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/myrg/providers/Microsoft.Compute/galleries/mygallery/images/myimage" +} +``` + +### ๐Ÿ’ป **Finding Available SKUs** + +DevBox SKUs determine the virtual machine size and performance characteristics. + +#### **Common SKU Names** +```hcl +# General Purpose - Balanced CPU, Memory, and Storage +"general_i_8c32gb256ssd_v2" # 8 vCPU, 32 GB RAM, 256 GB SSD +"general_i_16c64gb512ssd_v2" # 16 vCPU, 64 GB RAM, 512 GB SSD +"general_i_32c128gb1024ssd_v2" # 32 vCPU, 128 GB RAM, 1024 GB SSD + +# Memory Optimized - Higher RAM for memory-intensive workloads +"general_i_8c64gb256ssd_v2" # 8 vCPU, 64 GB RAM, 256 GB SSD +"general_i_16c128gb512ssd_v2" # 16 vCPU, 128 GB RAM, 512 GB SSD + +# Compute Optimized - Higher CPU for compute-intensive workloads +"general_i_8c16gb256ssd_v2" # 8 vCPU, 16 GB RAM, 256 GB SSD +"general_i_16c32gb512ssd_v2" # 16 vCPU, 32 GB RAM, 512 GB SSD +``` + +#### **Complete SKU Reference Table** +| SKU Name | vCPUs | RAM (GB) | Storage (GB) | Use Case | Cost Tier | +|----------|-------|----------|--------------|----------|-----------| +| `general_i_4c16gb128ssd_v2` | 4 | 16 | 128 | Light development, testing | Low | +| `general_i_8c32gb256ssd_v2` | 8 | 32 | 256 | Standard development | Medium | +| `general_i_8c64gb256ssd_v2` | 8 | 64 | 256 | Memory-intensive apps | Medium-High | +| `general_i_16c64gb512ssd_v2` | 16 | 64 | 512 | Heavy development, IDEs | High | +| `general_i_16c128gb512ssd_v2`| 16 | 128 | 512 | Big data, large datasets | High | +| `general_i_32c128gb1024ssd_v2`| 32| 128 | 1024 | AI/ML, enterprise dev | Very High | + +#### **SKU Selection Guidelines** +- **๐Ÿ‘ฅ Team Size**: Larger teams may benefit from fewer, more powerful DevBoxes +- **๐Ÿ› ๏ธ Workload Type**: Match SKU to your development requirements +- **๐Ÿ’ฐ Budget**: Balance performance needs with cost constraints +- **โฑ๏ธ Usage Pattern**: Consider if DevBoxes will be used continuously or intermittently + +#### **List Available SKUs by Region** +```bash +# List DevBox SKUs available (subscription-wide, no location filter) +az devcenter admin sku list + +# List DevBox SKUs with output formatting +az devcenter admin sku list --output table + +# Filter DevBox SKUs for specific families +az devcenter admin sku list --query "[?contains(name, 'general_i')]" + +# Alternative: List VM SKUs by location for sizing reference +az vm list-skus --location "East US" --resource-type "virtualMachines" --query "[?contains(name, 'Standard_D')]" +``` + +#### **Advanced SKU Configuration** +```hcl +# Simple SKU (recommended for most use cases) +sku_name = "general_i_16c64gb512ssd_v2" + +# Advanced SKU with additional properties +sku = { + name = "general_i_16c64gb512ssd_v2" + tier = "Standard" # Free, Basic, Standard, Premium +} + +# Note: Additional fields like family, size, and capacity are optional +# and should only be used if specifically required by your DevCenter configuration +``` + +#### **SKU Configuration Guidelines** +- **Simple SKU**: Use `sku_name` for straightforward configurations (recommended) +- **Advanced SKU**: Use `sku` object when you need to specify additional properties like tier +- **Required Fields**: Only `name` is required in the SKU object +- **Optional Fields**: `tier`, `family`, `size`, and `capacity` can be omitted for most use cases + +### ๐Ÿ’พ **OS Storage Types** + +The `os_storage_type` property specifies the storage configuration for the OS disk. + +#### **Available Storage Types** +```hcl +# Standard SSD options +os_storage_type = "ssd_256gb" # 256 GB Standard SSD +os_storage_type = "ssd_512gb" # 512 GB Standard SSD +os_storage_type = "ssd_1024gb" # 1024 GB Standard SSD + +# Premium SSD options (if supported by SKU) +os_storage_type = "premium_256gb" # 256 GB Premium SSD +os_storage_type = "premium_512gb" # 512 GB Premium SSD +os_storage_type = "premium_1024gb" # 1024 GB Premium SSD +``` + +#### **Storage Type Guidelines** +- **Standard SSD**: Cost-effective, good performance for most development workloads +- **Premium SSD**: Higher performance, better for I/O intensive applications +- **Size Selection**: Consider your development tools, datasets, and build artifacts storage needs + +### ๐Ÿ” **Discovery Commands** + +#### **Azure CLI Commands for Discovery** +```bash +# List DevCenters in subscription +az devcenter admin devcenter list + +# List available DevBox definitions +az devcenter admin devbox-definition list --dev-center-name "mydevcenter" --resource-group "myrg" + +# Get DevBox definition details +az devcenter admin devbox-definition show --dev-center-name "mydevcenter" --resource-group "myrg" --name "mydevbox" + +# List available images in a gallery +az devcenter admin image list --dev-center-name "mydevcenter" --resource-group "myrg" --gallery-name "default" + +# Check image version details +az devcenter admin image-version list --dev-center-name "mydevcenter" --resource-group "myrg" --gallery-name "default" --image-name "microsoftvisualstudio_visualstudioplustools_vs-2022-ent-general-win11-m365-gen2" + +# List DevBox SKUs with detailed output +az devcenter admin sku list --output table + +# Get specific SKU details with capabilities +az devcenter admin sku list --query "[?name=='general_i_16c64gb512ssd_v2']" + +# Check what image templates are available with structured output +az devcenter admin image list --dev-center-name "mydevcenter" --resource-group "myrg" --gallery-name "default" --query "[].{Name:name,Publisher:publisher,Offer:offer,Sku:sku}" + +# Validate if a specific image exists +az devcenter admin image show --dev-center-name "mydevcenter" --resource-group "myrg" --gallery-name "default" --name "microsoftvisualstudio_visualstudioplustools_vs-2022-ent-general-win11-m365-gen2" + +# List VM SKUs by location (for reference/comparison) +az vm list-skus --location "East US" --resource-type "virtualMachines" --output table +``` + +#### **PowerShell Commands for Discovery** +```powershell +# Install the Az.DevCenter module if not already installed +# Install-Module -Name Az.DevCenter -Scope CurrentUser -Force + +# List available DevBox SKUs (note: this may not be DevCenter-specific) +Get-AzComputeResourceSku | Where-Object {$_.ResourceType -eq "virtualMachines"} | Format-Table Name, Locations, Restrictions + +# Get DevCenter information +Get-AzDevCenterAdminDevCenter -ResourceGroupName "myrg" + +# List DevBox definitions +Get-AzDevCenterAdminDevBoxDefinition -DevCenterName "mydevcenter" -ResourceGroupName "myrg" + +# Get available images in default gallery +Get-AzDevCenterAdminImage -DevCenterName "mydevcenter" -ResourceGroupName "myrg" -GalleryName "default" + +# Check specific image details +Get-AzDevCenterAdminImage -DevCenterName "mydevcenter" -ResourceGroupName "myrg" -GalleryName "default" -Name "microsoftvisualstudio_visualstudioplustools_vs-2022-ent-general-win11-m365-gen2" + +# List available VM sizes for reference +Get-AzVMSize -Location "East US" | Where-Object {$_.Name -like "*Standard_D*"} | Format-Table Name, NumberOfCores, MemoryInMB, OSDiskSizeInMB + +# Get subscription context +Get-AzContext | Format-List Name, Account, Environment, Subscription +``` ## Simple Usage @@ -30,11 +287,20 @@ module "dev_box_definition" { use_slug = true } + location = "East US" dev_center_id = "/subscriptions/.../devcenters/mydevcenter" + dev_box_definition = { name = "win11-dev" - image_reference_id = "/subscriptions/.../galleries/mygallery/images/win11/versions/latest" + image_reference_id = "galleries/default/images/microsoftvisualstudio_visualstudioplustools_vs-2022-ent-general-win11-m365-gen2" sku_name = "general_i_8c32gb256ssd_v2" + + hibernate_support = true + + tags = { + purpose = "development" + team = "engineering" + } } } ``` @@ -57,20 +323,20 @@ module "dev_box_definition" { dev_box_definition = { name = "ai-development-box" - + # Built-in Azure DevCenter image (recommended) image_reference_id = "galleries/default/images/microsoftvisualstudio_visualstudioplustools_vs-2022-ent-general-win11-m365-gen2" - + # Or custom gallery image # image_reference = { # id = "galleries/mygallery/images/ai-dev-image" # } - sku_name = "general_i_32c128gb1024ssd_v2" - - hibernate_support = { - enabled = true - } - + + # Simple SKU configuration + sku_name = "general_i_32c128gb1024ssd_v2" + + hibernate_support = true + tags = { purpose = "ai-development" cost_center = "engineering" @@ -85,8 +351,183 @@ module "dev_box_definition" { } ``` +## Advanced SKU Object Usage + +```hcl +module "enterprise_dev_box_definition" { + source = "./modules/dev_center_dev_box_definition" + + global_settings = { + prefixes = ["enterprise"] + random_length = 3 + passthrough = false + use_slug = true + } + + location = "eastus" + dev_center_id = "/subscriptions/.../devcenters/mydevcenter" + + dev_box_definition = { + name = "enterprise-development-box" + + # Built-in Azure DevCenter image + image_reference_id = "galleries/default/images/microsoftvisualstudio_visualstudioplustools_vs-2022-pro-general-win11-m365-gen2" + + # Advanced SKU object configuration + sku = { + name = "general_i_16c64gb512ssd_v2" + tier = "Standard" + } + + hibernate_support = true + + os_storage_type = "ssd_512gb" + + tags = { + purpose = "enterprise-development" + cost_center = "engineering" + sku_config = "advanced" + } + } + + tags = { + managed_by = "terraform" + module = "dev_center_dev_box_definition" + } +} +``` + For more examples, see the [DevBox Definition examples](../../../examples/dev_center_dev_box_definition/). +## Ready-to-Use Configuration Templates + +### ๐Ÿข **Enterprise .NET Development** +```hcl +module "enterprise_dotnet_devbox" { + source = "./modules/dev_center_dev_box_definition" + + global_settings = { + prefixes = ["corp"] + random_length = 3 + passthrough = false + use_slug = true + } + + location = "East US" + dev_center_id = "/subscriptions/your-subscription-id/resourceGroups/your-rg/providers/Microsoft.DevCenter/devcenters/your-devcenter" + + dev_box_definition = { + name = "enterprise-dotnet-dev" + image_reference_id = "galleries/default/images/microsoftvisualstudio_visualstudioplustools_vs-2022-ent-general-win11-m365-gen2" + sku_name = "general_i_16c64gb512ssd_v2" + os_storage_type = "ssd_512gb" + + hibernate_support = true + + tags = { + purpose = "enterprise-development" + team = "engineering" + cost_center = "IT" + } + } +} +``` + +### ๐Ÿง **Windows Development Environment** +```hcl +module "windows_devbox" { + source = "./modules/dev_center_dev_box_definition" + + global_settings = { + prefixes = ["dev"] + random_length = 2 + passthrough = false + use_slug = true + } + + location = "West US 2" + dev_center_id = "/subscriptions/your-subscription-id/resourceGroups/your-rg/providers/Microsoft.DevCenter/devcenters/your-devcenter" + + dev_box_definition = { + name = "windows-dev-env" + image_reference_id = "galleries/default/images/microsoftwindowsdesktop_windows-ent-cpc_win11-24h2-ent-cpc-m365" + sku_name = "general_i_8c32gb256ssd_v2" + os_storage_type = "ssd_256gb" + + hibernate_support = true + + tags = { + os_type = "windows" + purpose = "development" + } + } +} +``` + +### ๐Ÿค– **AI/ML Development Workstation** +```hcl +module "ai_ml_devbox" { + source = "./modules/dev_center_dev_box_definition" + + global_settings = { + prefixes = ["ai"] + random_length = 4 + passthrough = false + use_slug = true + } + + location = "East US" + dev_center_id = "/subscriptions/your-subscription-id/resourceGroups/your-rg/providers/Microsoft.DevCenter/devcenters/your-devcenter" + + dev_box_definition = { + name = "ai-ml-workstation" + image_reference_id = "galleries/default/images/microsoftvisualstudio_windowsplustools_base-win11-gen2" + sku_name = "general_i_32c128gb1024ssd_v2" + os_storage_type = "premium_1024gb" + + hibernate_support = true # Save costs when not actively training models + + tags = { + workload_type = "ai-ml" + gpu_required = "false" + cost_center = "research" + } + } +} +``` + +### ๐Ÿงช **Testing and CI/CD Environment** +```hcl +module "testing_devbox" { + source = "./modules/dev_center_dev_box_definition" + + global_settings = { + prefixes = ["test"] + random_length = 2 + passthrough = false + use_slug = true + } + + location = "Central US" + dev_center_id = "/subscriptions/your-subscription-id/resourceGroups/your-rg/providers/Microsoft.DevCenter/devcenters/your-devcenter" + + dev_box_definition = { + name = "automated-testing" + image_reference_id = "galleries/default/images/microsoftwindowsdesktop_windows-ent-cpc_win11-24h2-ent-cpc" + sku_name = "general_i_4c16gb128ssd_v2" # Minimal resources for testing + os_storage_type = "ssd_128gb" + + hibernate_support = false # Testing environments need quick startup + + tags = { + purpose = "automated-testing" + environment = "ci-cd" + shutdown = "auto" + } + } +} +``` + ## Resources - Azure DevCenter DevBox Definition (`Microsoft.DevCenter/devcenters/devboxdefinitions`) @@ -102,14 +543,14 @@ This module implements the [Microsoft.DevCenter/devcenters/devboxdefinitions](ht |------|---------| | [terraform](#requirement\_terraform) | >= 1.9.0 | | [azapi](#requirement\_azapi) | ~> 2.4.0 | -| [azurecaf](#requirement\_azurecaf) | ~> 1.2.0 | +| [azurecaf](#requirement\_azurecaf) | ~> 1.2.29 | ## Providers | Name | Version | |------|---------| | [azapi](#provider\_azapi) | 2.4.0 | -| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurecaf](#provider\_azurecaf) | 1.2.29 | ## Modules @@ -121,28 +562,35 @@ No modules. |------|------| | [azapi_resource.dev_box_definition](https://registry.terraform.io/providers/Azure/azapi/latest/docs/resources/resource) | resource | | [azurecaf_name.dev_box_definition](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azapi_client_config.current](https://registry.terraform.io/providers/Azure/azapi/latest/docs/data-sources/client_config) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [dev\_box\_definition](#input\_dev\_box\_definition) | Configuration object for the DevBox Definition |
object({
name = string

# Image reference - supports both direct ID and object form
image_reference_id = optional(string)
image_reference = optional(object({
id = string
}))

# SKU configuration - storage is defined within the SKU name itself
sku_name = string

# Hibernate support
hibernate_support = optional(object({
enabled = optional(bool, false)
}))

# Tags
tags = optional(map(string), {})
})
| n/a | yes | +| [dev\_box\_definition](#input\_dev\_box\_definition) | Configuration object for the DevBox Definition |
object({
name = string

# Image reference - supports both direct ID and object form
image_reference_id = optional(string)
image_reference = optional(object({
id = string
}))

# SKU configuration - supports both simple name and full object
sku_name = optional(string)
sku = optional(object({
name = string # Required: The name of the SKU
capacity = optional(number) # Optional: Integer for scale out/in support
family = optional(string) # Optional: Hardware generation
size = optional(string) # Optional: Standalone SKU size code
tier = optional(string) # Optional: Free, Basic, Standard, Premium
}))

# OS Storage type for the Operating System disk
os_storage_type = optional(string)

# Hibernate support - simplified boolean (maps to "Enabled"/"Disabled" in API)
hibernate_support = optional(bool, false)

# Tags
tags = optional(map(string), {})
})
| n/a | yes | | [dev\_center\_id](#input\_dev\_center\_id) | The ID of the Dev Center where the DevBox Definition will be created | `string` | n/a | yes | | [global\_settings](#input\_global\_settings) | Global settings object for naming conventions and standard parameters |
object({
prefixes = list(string)
random_length = number
passthrough = bool
use_slug = bool
tags = optional(map(string), {})
})
| n/a | yes | +| [location](#input\_location) | The Azure region where the DevBox Definition will be created | `string` | n/a | yes | | [tags](#input\_tags) | Additional tags to apply to the DevBox Definition | `map(string)` | `{}` | no | ## Outputs | Name | Description | |------|-------------| +| [active\_image\_reference](#output\_active\_image\_reference) | Image reference information for the currently active image | | [dev\_center\_id](#output\_dev\_center\_id) | The ID of the Dev Center | | [hibernate\_support](#output\_hibernate\_support) | The hibernate support status | | [id](#output\_id) | The ID of the DevBox Definition | | [image\_reference](#output\_image\_reference) | The image reference configuration | +| [image\_validation\_error\_details](#output\_image\_validation\_error\_details) | Details for image validator error | +| [image\_validation\_status](#output\_image\_validation\_status) | Validation status of the configured image | | [name](#output\_name) | The name of the DevBox Definition | +| [os\_storage\_type](#output\_os\_storage\_type) | The storage type used for the Operating System disk | | [provisioning\_state](#output\_provisioning\_state) | The provisioning state of the DevBox Definition | | [sku](#output\_sku) | The SKU configuration | | [tags](#output\_tags) | The tags assigned to the DevBox Definition | +| [validation\_status](#output\_validation\_status) | Validation status for the Dev Box Definition | ## Validation Rules @@ -152,6 +600,144 @@ The module includes comprehensive validation for: - **DevBox Definition Name**: Must be 63 characters or less and follow Azure naming conventions - **Image Reference**: Either `image_reference_id` or `image_reference` object must be provided - **Dev Center ID**: Must be a valid Azure resource ID format +- **SKU Configuration**: When using SKU object, `name` field is required +- **SKU Tier**: Must be one of: `Free`, `Basic`, `Standard`, `Premium` +- **SKU Capacity**: Must be a positive integer when specified +- **OS Storage Type**: Must follow pattern `(ssd|premium)_(128|256|512|1024)gb` + +### Input Validation Examples + +#### โœ… **Valid Configurations:** +```hcl +# Valid OS storage types +os_storage_type = "ssd_256gb" +os_storage_type = "premium_512gb" +os_storage_type = "ssd_1024gb" + +# Valid SKU configurations +sku_name = "general_i_16c64gb512ssd_v2" + +sku = { + name = "general_i_16c64gb512ssd_v2" + tier = "Standard" + capacity = 1 +} +``` + +#### โŒ **Invalid Configurations:** +```hcl +# Invalid OS storage type +os_storage_type = "invalid_storage" # Error: Must match pattern + +# Invalid SKU tier +sku = { + name = "general_i_16c64gb512ssd_v2" + tier = "InvalidTier" # Error: Must be Free, Basic, Standard, or Premium +} + +# Invalid SKU capacity +sku = { + name = "general_i_16c64gb512ssd_v2" + capacity = -1 # Error: Must be positive integer +} +``` + +## Troubleshooting Guide + +### ๐Ÿ”ง **Common Issues and Solutions** + +#### **SKU Not Available Error** +``` +Error: The SKU 'general_i_16c64gb512ssd_v2' is not available in region 'West Europe' +``` + +**Solution**: Check SKU availability in your target region: +```bash +# Check which DevBox SKUs are available (subscription-wide) +az devcenter admin sku list --query "[].name" -o table + +# Find alternative DevBox SKUs with similar specs +az devcenter admin sku list --query "[?contains(name, '16c')]" + +# Check VM SKUs available in your region (for comparison) +az vm list-skus --location "West Europe" --resource-type "virtualMachines" --query "[?contains(name, 'Standard_D')]" +``` + +#### **Image Not Found Error** +``` +Error: The specified image 'galleries/default/images/nonexistent-image' could not be found +``` + +**Solution**: Verify image availability and get the correct name: +```bash +# List all available images in the default gallery +az devcenter admin image list --dev-center-name "mydevcenter" --resource-group "myrg" --gallery-name "default" --query "[].name" -o table + +# Search for specific image patterns +az devcenter admin image list --dev-center-name "mydevcenter" --resource-group "myrg" --gallery-name "default" --query "[?contains(name, 'visualstudio')]" +``` + +#### **Hibernate Support Compatibility** +Not all SKUs support hibernation. Check SKU capabilities: +```bash +# Check if a SKU supports hibernation +az devcenter admin sku list --query "[?name=='general_i_8c32gb256ssd_v2'].capabilities" + +# List all SKUs with hibernation support +az devcenter admin sku list --query "[?capabilities[?name=='HibernateSupport' && value=='true']].name" -o table +``` + +#### **Storage Type Validation** +Ensure the `os_storage_type` matches the SKU's storage configuration: +```hcl +# For SKUs with 256GB storage, use matching storage type +sku_name = "general_i_8c32gb256ssd_v2" +os_storage_type = "ssd_256gb" # โœ… Correct - matches SKU storage + +# This would be incorrect: +# os_storage_type = "ssd_512gb" # โŒ Wrong - doesn't match SKU +``` + +### ๐Ÿ” **Validation Commands** + +Before applying your configuration, validate your choices: + +```bash +# Validate DevCenter exists and is accessible +az devcenter admin devcenter show --name "mydevcenter" --resource-group "myrg" + +# Check if the image exists in the gallery +az devcenter admin image show --dev-center-name "mydevcenter" --resource-group "myrg" --gallery-name "default" --name "YOUR_IMAGE_NAME" + +# Verify DevBox SKU availability +az devcenter admin sku list --query "[?name=='YOUR_SKU_NAME']" + +# Alternative: Check VM SKU availability in target region +az vm list-skus --location "YOUR_REGION" --resource-type "virtualMachines" --query "[?name=='YOUR_VM_SIZE']" + +# Test terraform configuration +terraform plan -var-file="configuration.tfvars" +``` + +### ๐Ÿš€ **Performance Optimization Tips** + +#### **Choosing the Right SKU** +- **Development**: `general_i_8c32gb256ssd_v2` - Good for general development work +- **Heavy Development**: `general_i_16c64gb512ssd_v2` - Visual Studio, large projects +- **AI/ML Development**: `general_i_32c128gb1024ssd_v2` - Data science, model training +- **Testing/CI**: `general_i_4c16gb128ssd_v2` - Automated testing workloads + +#### **Storage Considerations** +- **SSD vs Premium**: Use Premium SSD for I/O intensive workloads +- **Size Planning**: Consider OS (40-60GB) + Tools (20-40GB) + Projects (remaining) +- **Cost vs Performance**: Balance storage performance with budget requirements + +#### **Regional Selection** +Choose regions based on: +- **Latency**: Closest to your users/developers +- **SKU Availability**: Some advanced SKUs may not be available in all regions +- **Cost**: Pricing may vary between regions +- **Compliance**: Data residency requirements ## Automatic Subscription ID Resolution diff --git a/modules/dev_center_dev_box_definition/module.tf b/modules/dev_center_dev_box_definition/module.tf index c0ef193..e10501b 100644 --- a/modules/dev_center_dev_box_definition/module.tf +++ b/modules/dev_center_dev_box_definition/module.tf @@ -3,7 +3,7 @@ terraform { required_providers { azurecaf = { source = "aztfmod/azurecaf" - version = "~> 1.2.0" + version = "~> 1.2.29" } azapi = { source = "Azure/azapi" @@ -48,11 +48,22 @@ locals { var.dev_box_definition.image_reference.id ) } : null + + # Build SKU object for advanced configuration - filter null values to avoid type issues + sku_object = var.dev_box_definition.sku != null ? merge( + { + name = var.dev_box_definition.sku.name + }, + var.dev_box_definition.sku.capacity != null ? { capacity = var.dev_box_definition.sku.capacity } : {}, + var.dev_box_definition.sku.family != null ? { family = var.dev_box_definition.sku.family } : {}, + var.dev_box_definition.sku.size != null ? { size = var.dev_box_definition.sku.size } : {}, + var.dev_box_definition.sku.tier != null ? { tier = var.dev_box_definition.sku.tier } : {} + ) : null } resource "azurecaf_name" "dev_box_definition" { name = var.dev_box_definition.name - resource_type = "general" + resource_type = "azurerm_dev_center_dev_box_definition" prefixes = var.global_settings.prefixes random_length = var.global_settings.random_length clean_input = true @@ -68,8 +79,8 @@ resource "azapi_resource" "dev_box_definition" { tags = local.tags response_export_values = ["properties.provisioningState", "properties.imageReference", "properties.sku"] - # Disable schema validation as the provider validation is overly strict for preview APIs - schema_validation_enabled = false + # Enable schema validation + schema_validation_enabled = true body = { properties = merge( # Image reference configuration @@ -79,16 +90,25 @@ resource "azapi_resource" "dev_box_definition" { imageReference = { id = local.processed_image_reference_id } - } : {}, # SKU configuration - { + } : {}, + + # SKU configuration - supports both simple name and full object + local.sku_object != null ? { + sku = local.sku_object + } : var.dev_box_definition.sku_name != null ? { sku = { name = var.dev_box_definition.sku_name } - }, + } : {}, + + # OS Storage Type configuration + try(var.dev_box_definition.os_storage_type, null) != null ? { + osStorageType = var.dev_box_definition.os_storage_type + } : {}, # Hibernate support configuration - try(var.dev_box_definition.hibernate_support, null) != null ? { - hibernateSupport = try(var.dev_box_definition.hibernate_support.enabled, false) ? "Enabled" : "Disabled" + var.dev_box_definition.hibernate_support != null ? { + hibernateSupport = var.dev_box_definition.hibernate_support ? "Enabled" : "Disabled" } : {} ) } diff --git a/modules/dev_center_dev_box_definition/output.tf b/modules/dev_center_dev_box_definition/output.tf index 3a5bc7a..c5349fd 100644 --- a/modules/dev_center_dev_box_definition/output.tf +++ b/modules/dev_center_dev_box_definition/output.tf @@ -35,6 +35,31 @@ output "hibernate_support" { value = try(azapi_resource.dev_box_definition.output.properties.hibernateSupport, null) } +output "os_storage_type" { + description = "The storage type used for the Operating System disk" + value = try(azapi_resource.dev_box_definition.output.properties.osStorageType, null) +} + +output "image_validation_status" { + description = "Validation status of the configured image" + value = try(azapi_resource.dev_box_definition.output.properties.imageValidationStatus, null) +} + +output "image_validation_error_details" { + description = "Details for image validator error" + value = try(azapi_resource.dev_box_definition.output.properties.imageValidationErrorDetails, null) +} + +output "validation_status" { + description = "Validation status for the Dev Box Definition" + value = try(azapi_resource.dev_box_definition.output.properties.validationStatus, null) +} + +output "active_image_reference" { + description = "Image reference information for the currently active image" + value = try(azapi_resource.dev_box_definition.output.properties.activeImageReference, null) +} + output "tags" { description = "The tags assigned to the DevBox Definition" value = azapi_resource.dev_box_definition.tags diff --git a/modules/dev_center_dev_box_definition/variables.tf b/modules/dev_center_dev_box_definition/variables.tf index 2091773..128233d 100644 --- a/modules/dev_center_dev_box_definition/variables.tf +++ b/modules/dev_center_dev_box_definition/variables.tf @@ -33,14 +33,24 @@ variable "dev_box_definition" { image_reference_id = optional(string) image_reference = optional(object({ id = string - })) # SKU configuration - storage is defined within the SKU name itself - sku_name = string + })) - # Hibernate support - hibernate_support = optional(object({ - enabled = optional(bool, false) + # SKU configuration - supports both simple name and full object + sku_name = optional(string) + sku = optional(object({ + name = string # Required: The name of the SKU + capacity = optional(number) # Optional: Integer for scale out/in support + family = optional(string) # Optional: Hardware generation + size = optional(string) # Optional: Standalone SKU size code + tier = optional(string) # Optional: Free, Basic, Standard, Premium })) + # OS Storage type for the Operating System disk + os_storage_type = optional(string) + + # Hibernate support - simplified boolean (maps to "Enabled"/"Disabled" in API) + hibernate_support = optional(bool, false) + # Tags tags = optional(map(string), {}) }) @@ -53,6 +63,39 @@ variable "dev_box_definition" { error_message = "Either image_reference_id or image_reference must be specified." } + validation { + condition = ( + var.dev_box_definition.sku_name != null || + var.dev_box_definition.sku != null + ) + error_message = "Either sku_name or sku must be specified." + } + + validation { + condition = ( + var.dev_box_definition.sku == null || + var.dev_box_definition.sku.name != null + ) + error_message = "When using sku object, the 'name' field is required." + } + + validation { + condition = ( + var.dev_box_definition.sku == null || + var.dev_box_definition.sku.tier == null || + contains(["Free", "Basic", "Standard", "Premium"], var.dev_box_definition.sku.tier) + ) + error_message = "SKU tier must be one of: Free, Basic, Standard, Premium." + } + + validation { + condition = ( + var.dev_box_definition.sku == null || + var.dev_box_definition.sku.capacity == null || + var.dev_box_definition.sku.capacity >= 1 + ) + error_message = "SKU capacity must be a positive integer when specified." + } validation { condition = length(var.dev_box_definition.name) <= 63 @@ -63,6 +106,14 @@ variable "dev_box_definition" { condition = can(regex("^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$", var.dev_box_definition.name)) error_message = "DevBox Definition name must start and end with alphanumeric characters and can contain hyphens." } + + validation { + condition = ( + var.dev_box_definition.os_storage_type == null || + can(regex("^(ssd|premium)_(128|256|512|1024)gb$", var.dev_box_definition.os_storage_type)) + ) + error_message = "OS storage type must follow the pattern: (ssd|premium)_(128|256|512|1024)gb (e.g., 'ssd_256gb', 'premium_512gb')." + } } variable "tags" { diff --git a/modules/dev_center_project/README.md b/modules/dev_center_project/README.md index 2f0f91d..4558f62 100644 --- a/modules/dev_center_project/README.md +++ b/modules/dev_center_project/README.md @@ -118,7 +118,7 @@ For more examples, see the [Dev Center Project examples](../../../examples/dev_c | Name | Version | |------|---------| | [azapi](#provider\_azapi) | 2.4.0 | -| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurecaf](#provider\_azurecaf) | 1.2.29 | ## Modules diff --git a/tests/run_tests.sh b/tests/run_tests.sh index caae0d8..ef547f9 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -61,7 +61,7 @@ echo -e "----------------\n" failed_tests=() # Run all unit tests -unit_test_dirs=("tests/unit/resource_group" "tests/unit/dev_center" "tests/unit/dev_center_environment_type" "tests/unit/dev_center_project" "tests/unit/dev_center_catalog") +unit_test_dirs=("tests/unit/resource_group" "tests/unit/dev_center" "tests/unit/dev_center_dev_box_definition" "tests/unit/dev_center_environment_type" "tests/unit/dev_center_project" "tests/unit/dev_center_catalog") for dir in "${unit_test_dirs[@]}"; do test_name=$(basename "$dir") diff --git a/tests/unit/dev_center_dev_box_definition/README.md b/tests/unit/dev_center_dev_box_definition/README.md new file mode 100644 index 0000000..9cf60c6 --- /dev/null +++ b/tests/unit/dev_center_dev_box_definition/README.md @@ -0,0 +1,82 @@ +# DevBox Definition Unit Tests + +This directory contains comprehensive unit tests for the Azure DevCenter DevBox Definition module. + +## Test Coverage + +The `devbox_definition_test.tftest.hcl` file includes the following test scenarios: + +### 1. `test_basic_devbox_definition_with_id` +- Tests basic DevBox definition creation using `image_reference_id` +- Verifies that a simple DevBox definition with mandatory fields is created successfully +- Uses basic SKU name and hibernate support enabled + +### 2. `test_devbox_definition_with_object` +- Tests DevBox definition creation using `image_reference` object format +- Verifies the alternative image reference configuration method +- Uses basic SKU name with hibernate support disabled + +### 3. `test_hibernate_support` +- Specifically tests hibernate support configuration +- Verifies that hibernate support can be enabled for DevBox definitions +- Tests the boolean to string conversion for the Azure API + +### 4. `test_storage_type` +- Tests OS storage type configuration +- Verifies that custom storage types can be specified +- Uses `os_storage_type` field with SSD configuration + +### 5. `test_advanced_sku` +- Tests advanced SKU configuration using the SKU object +- Verifies that complex SKU configurations with name and tier work properly +- Tests the structured SKU approach vs simple SKU name + +### 6. `test_naming_convention` +- Tests the azurecaf naming convention integration +- Verifies that different global settings affect resource naming +- Uses different prefixes and random length settings + +### 7. `test_multiple_definitions` +- Tests multiple DevBox definitions in a single configuration +- Verifies that different configurations can coexist +- Tests both image reference formats and SKU configurations simultaneously + +## Test Infrastructure + +The tests use: +- Mock providers for `azapi` and `azurecaf` +- Predefined resource groups and dev centers for testing +- All required root module variables to ensure proper isolation + +## Running Tests + +### Individual Test File +```bash +cd tests/unit/dev_center_dev_box_definition +terraform test devbox_definition_test.tftest.hcl -verbose +``` + +### All DevBox Definition Tests +```bash +# From root directory (change to test directory and back) +cd tests/unit/dev_center_dev_box_definition && terraform test devbox_definition_test.tftest.hcl -verbose && cd ../../.. +``` + +### All Unit Tests +```bash +./tests/run_tests.sh +``` + +## Test Results + +All tests should pass successfully: +- โœ… 7 tests passing +- โœ… 0 tests failing +- โœ… Complete coverage of module functionality + +## Notes + +- The tests use mock Azure subscription IDs and resource paths +- Each test runs in isolation with its own variable overrides +- Tests verify both the creation logic and the output structure +- Schema validation is enabled to ensure Azure API compatibility diff --git a/tests/unit/dev_center_dev_box_definition/devbox_definition_test.tftest.hcl b/tests/unit/dev_center_dev_box_definition/devbox_definition_test.tftest.hcl index 2136840..22bbac3 100644 --- a/tests/unit/dev_center_dev_box_definition/devbox_definition_test.tftest.hcl +++ b/tests/unit/dev_center_dev_box_definition/devbox_definition_test.tftest.hcl @@ -44,9 +44,7 @@ variables { } image_reference_id = "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Compute/galleries/testgallery/images/testimage/versions/latest" sku_name = "general_i_8c32gb256ssd_v2" - hibernate_support = { - enabled = true - } + hibernate_support = true tags = { environment = "test" module = "dev_center_dev_box_definition" @@ -65,10 +63,8 @@ variables { image_reference = { id = "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Compute/galleries/testgallery/images/testimage2/versions/1.0.0" } - sku_name = "general_i_16c64gb512ssd_v2" - hibernate_support = { - enabled = false - } + sku_name = "general_i_16c64gb512ssd_v2" + hibernate_support = false tags = { environment = "test" module = "dev_center_dev_box_definition" @@ -103,40 +99,127 @@ mock_provider "azurecaf" {} run "test_basic_devbox_definition_with_id" { command = plan - module { - source = "../../../" + providers = { + azapi = azapi + azurecaf = azurecaf } + variables { + // Override with only the first definition + dev_center_dev_box_definitions = { + definition1 = { + name = "test-definition-1" + dev_center = { + key = "devcenter1" + } + resource_group = { + key = "rg1" + } + image_reference_id = "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Compute/galleries/testgallery/images/testimage/versions/latest" + sku_name = "general_i_8c32gb256ssd_v2" + hibernate_support = true + tags = { + environment = "test" + module = "dev_center_dev_box_definition" + } + } + } + } + + module { source = "../../../" } + assert { condition = module.dev_center_dev_box_definitions["definition1"] != null error_message = "DevBox Definition with image_reference_id should be created" } + + assert { + condition = length(keys(module.dev_center_dev_box_definitions)) == 1 + error_message = "Should only have one DevBox definition" + } } // Test for DevBox Definition creation with image_reference object run "test_devbox_definition_with_object" { command = plan - module { - source = "../../../" + providers = { + azapi = azapi + azurecaf = azurecaf + } + + variables { + // Override with only the second definition + dev_center_dev_box_definitions = { + definition2 = { + name = "test-definition-2" + dev_center = { + key = "devcenter1" + } + resource_group = { + key = "rg1" + } + image_reference = { + id = "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Compute/galleries/testgallery/images/testimage2/versions/1.0.0" + } + sku_name = "general_i_16c64gb512ssd_v2" + hibernate_support = false + tags = { + environment = "test" + module = "dev_center_dev_box_definition" + test_case = "image_reference_object" + } + } + } } + module { source = "../../../" } + assert { condition = module.dev_center_dev_box_definitions["definition2"] != null error_message = "DevBox Definition with image_reference object should be created" } + + assert { + condition = length(keys(module.dev_center_dev_box_definitions)) == 1 + error_message = "Should only have one DevBox definition (definition2)" + } } // Test hibernate support configuration run "test_hibernate_support" { command = plan - module { - source = "../../../" + providers = { + azapi = azapi + azurecaf = azurecaf } + variables { + // Test with hibernate enabled + dev_center_dev_box_definitions = { + hibernate_enabled = { + name = "test-hibernate-enabled" + dev_center = { + key = "devcenter1" + } + resource_group = { + key = "rg1" + } + image_reference_id = "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Compute/galleries/testgallery/images/testimage/versions/latest" + sku_name = "general_i_8c32gb256ssd_v2" + hibernate_support = true + tags = { + test_case = "hibernate_enabled" + } + } + } + } + + module { source = "../../../" } + assert { - condition = module.dev_center_dev_box_definitions["definition1"] != null + condition = module.dev_center_dev_box_definitions["hibernate_enabled"] != null error_message = "DevBox Definition with hibernate support should be planned for creation" } } @@ -145,12 +228,79 @@ run "test_hibernate_support" { run "test_storage_type" { command = plan - module { - source = "../../../" + providers = { + azapi = azapi + azurecaf = azurecaf + } + + variables { + // Test with OS storage type + dev_center_dev_box_definitions = { + storage_test = { + name = "test-storage-type" + dev_center = { + key = "devcenter1" + } + resource_group = { + key = "rg1" + } + image_reference_id = "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Compute/galleries/testgallery/images/testimage/versions/latest" + sku_name = "general_i_16c64gb512ssd_v2" + os_storage_type = "ssd_1024gb" + hibernate_support = false + tags = { + test_case = "storage_type" + } + } + } } + + module { source = "../../../" } + assert { - condition = module.dev_center_dev_box_definitions["definition2"] != null - error_message = "DevBox Definition with SKU should be planned for creation" + condition = module.dev_center_dev_box_definitions["storage_test"] != null + error_message = "DevBox Definition with OS storage type should be planned for creation" + } +} + +// Test advanced SKU configuration +run "test_advanced_sku" { + command = plan + + providers = { + azapi = azapi + azurecaf = azurecaf + } + + variables { + // Test with advanced SKU object + dev_center_dev_box_definitions = { + advanced_sku = { + name = "test-advanced-sku" + dev_center = { + key = "devcenter1" + } + resource_group = { + key = "rg1" + } + image_reference_id = "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Compute/galleries/testgallery/images/testimage/versions/latest" + sku = { + name = "general_i_32c128gb1024ssd_v2" + tier = "Standard" + } + hibernate_support = true + tags = { + test_case = "advanced_sku" + } + } + } + } + + module { source = "../../../" } + + assert { + condition = module.dev_center_dev_box_definitions["advanced_sku"] != null + error_message = "DevBox Definition with advanced SKU should be planned for creation" } } @@ -158,12 +308,106 @@ run "test_storage_type" { run "test_naming_convention" { command = plan - module { - source = "../../../" + providers = { + azapi = azapi + azurecaf = azurecaf + } + + variables { + // Test naming convention with different global settings + global_settings = { + prefixes = ["prod"] + random_length = 5 + passthrough = false + use_slug = true + } + + dev_center_dev_box_definitions = { + naming_test = { + name = "naming-convention-test" + dev_center = { + key = "devcenter1" + } + resource_group = { + key = "rg1" + } + image_reference_id = "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Compute/galleries/testgallery/images/testimage/versions/latest" + sku_name = "general_i_8c32gb256ssd_v2" + hibernate_support = false + tags = { + test_case = "naming_convention" + } + } + } } + module { source = "../../../" } + assert { - condition = module.dev_center_dev_box_definitions["definition1"] != null + condition = module.dev_center_dev_box_definitions["naming_test"] != null error_message = "DevBox Definition should be planned for creation with proper naming" } } + +// Test validation - multiple definitions with different configurations +run "test_multiple_definitions" { + command = plan + + providers = { + azapi = azapi + azurecaf = azurecaf + } + + variables { + // Test with multiple definitions + dev_center_dev_box_definitions = { + def1 = { + name = "multi-test-1" + dev_center = { + key = "devcenter1" + } + resource_group = { + key = "rg1" + } + image_reference_id = "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Compute/galleries/testgallery/images/testimage/versions/latest" + sku_name = "general_i_8c32gb256ssd_v2" + hibernate_support = true + } + def2 = { + name = "multi-test-2" + dev_center = { + key = "devcenter1" + } + resource_group = { + key = "rg1" + } + image_reference = { + id = "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Compute/galleries/testgallery/images/testimage2/versions/1.0.0" + } + sku = { + name = "general_i_16c64gb512ssd_v2" + tier = "Standard" + } + os_storage_type = "ssd_512gb" + hibernate_support = false + } + } + } + + module { source = "../../../" } + + assert { + condition = length(keys(module.dev_center_dev_box_definitions)) == 2 + error_message = "Should have exactly two DevBox definitions" + } + + assert { + condition = module.dev_center_dev_box_definitions["def1"] != null + error_message = "First DevBox definition should be created" + } + + assert { + condition = module.dev_center_dev_box_definitions["def2"] != null + error_message = "Second DevBox definition should be created" + } +} diff --git a/variables.tf b/variables.tf index 4ee69ad..e17a6ce 100644 --- a/variables.tf +++ b/variables.tf @@ -95,13 +95,21 @@ variable "dev_center_dev_box_definitions" { id = string })) - # SKU configuration - storage is defined within the SKU name itself - sku_name = string + # SKU configuration - supports both simple name and full object + sku_name = optional(string) + sku = optional(object({ + name = string + capacity = optional(number) + family = optional(string) + size = optional(string) + tier = optional(string) # Free, Basic, Standard, Premium + })) + + # OS Storage type for the Operating System disk + os_storage_type = optional(string) # Hibernate support - hibernate_support = optional(object({ - enabled = optional(bool, false) - })) + hibernate_support = optional(bool, false) # Tags tags = optional(map(string), {}) From f1cc961fd335adbf2da43b25e2b249400a6ff81b Mon Sep 17 00:00:00 2001 From: Arnaud Lheureux Date: Tue, 17 Jun 2025 13:57:07 +0800 Subject: [PATCH 4/6] refactor: Remove tflint ignore comment for dev_center_dev_box_definitions variable --- variables.tf | 1 - 1 file changed, 1 deletion(-) diff --git a/variables.tf b/variables.tf index e17a6ce..b4e9ef4 100644 --- a/variables.tf +++ b/variables.tf @@ -77,7 +77,6 @@ variable "dev_center_galleries" { default = {} } -# tflint-ignore: terraform_unused_declarations variable "dev_center_dev_box_definitions" { description = "Dev Center Dev Box Definitions configuration objects" type = map(object({ From a452690c95ede58db14e83689848d100c4156f21 Mon Sep 17 00:00:00 2001 From: Arnaud Lheureux Date: Tue, 17 Jun 2025 14:00:21 +0800 Subject: [PATCH 5/6] refactor: Clean up formatting in provider.tf by removing unnecessary blank lines --- provider.tf | 3 --- 1 file changed, 3 deletions(-) diff --git a/provider.tf b/provider.tf index 8e72d04..b4248fe 100644 --- a/provider.tf +++ b/provider.tf @@ -10,7 +10,4 @@ terraform { } } required_version = ">= 1.12.1" - -} - } From 63ae93938c0d759ee8cedf0afb3931577100da38 Mon Sep 17 00:00:00 2001 From: Arnaud Lheureux Date: Tue, 17 Jun 2025 14:16:33 +0800 Subject: [PATCH 6/6] fix: Add terraform init command to plan, apply, and destroy tasks in tasks.json --- .vscode/tasks.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 329873c..8ac25d4 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -7,7 +7,7 @@ "command": "/bin/bash", "args": [ "-c", - "export ARM_SUBSCRIPTION_ID=$(az account show --query id -o tsv) && terraform plan -var-file=${input:devCenterExample}" + "export ARM_SUBSCRIPTION_ID=$(az account show --query id -o tsv) && terraform init && terraform plan -var-file=${input:devCenterExample}" ], "options": { "cwd": "${workspaceFolder}" @@ -24,7 +24,7 @@ "command": "/bin/bash", "args": [ "-c", - "export ARM_SUBSCRIPTION_ID=$(az account show --query id -o tsv) && terraform apply -auto-approve -var-file=${input:devCenterExample}" + "export ARM_SUBSCRIPTION_ID=$(az account show --query id -o tsv) && terraform init && terraform apply -auto-approve -var-file=${input:devCenterExample}" ], "options": { "cwd": "${workspaceFolder}" @@ -37,7 +37,7 @@ "command": "/bin/bash", "args": [ "-c", - "export ARM_SUBSCRIPTION_ID=$(az account show --query id -o tsv) && terraform destroy -auto-approve -var-file=${input:devCenterExample}" + "export ARM_SUBSCRIPTION_ID=$(az account show --query id -o tsv) && terraform init && terraform destroy -auto-approve -var-file=${input:devCenterExample}" ], "options": { "cwd": "${workspaceFolder}"