From 2f921746c2e6acd9ec593e01807bef07e9cb5dc3 Mon Sep 17 00:00:00 2001 From: Rafferty Uy <1037626+raffertyuy@users.noreply.github.com> Date: Tue, 12 Aug 2025 08:29:19 +0000 Subject: [PATCH 1/3] Create a GitHub issue template for instructing the coding agent for implementing Terraform modeules. --- .github/ISSUE_TEMPLATE/new_tf_module.md | 17 ++++++++ .vscode/mcp.json | 2 +- README.md | 1 + docs/coding_agent.md | 55 +++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 .github/ISSUE_TEMPLATE/new_tf_module.md create mode 100644 docs/coding_agent.md diff --git a/.github/ISSUE_TEMPLATE/new_tf_module.md b/.github/ISSUE_TEMPLATE/new_tf_module.md new file mode 100644 index 0000000..24d02d1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/new_tf_module.md @@ -0,0 +1,17 @@ +--- +name: 'New Dev Factory Terraform Module' +about: 'Assign a new Dev Factory tf module implementation task to the GitHub Copilot Coding Agent' +title: 'Implement {module_name}' +labels: 'enhancement' +assignees: 'copilot-swe-agent[bot]' +--- + +Your goal is to implement the specified terraform module by following the steps below. + +## Module to implement +module_name=REPLACE_WITH_MODULE_NAME + +## STEPS +1. Read and follow the instructions in `.github/copilot-instructions.md`. +2. Create an implementation plan by running `/1-plan {module_name}`. This follows the prompt in `.github/prompts/1-plan.prompt.md` and outputs a new file: {module_name}.plan.md +3. Implement the created implementation plan by running `/2-implement #file:docs/plans/{module_name}.plan.md`. This follows the prompt in `.github/prompts/2-implement.prompt.md` for the plan created in step 2. \ No newline at end of file diff --git a/.vscode/mcp.json b/.vscode/mcp.json index 936753e..6061b9c 100644 --- a/.vscode/mcp.json +++ b/.vscode/mcp.json @@ -1,5 +1,5 @@ { - "servers": { + "servers": { "Terraform MCP Server": { "command": "docker", "args": [ diff --git a/README.md b/README.md index 6372bb2..9bdf8a8 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ The project includes comprehensive documentation to help you understand and use - [Getting Started Guide](docs/getting_started.md) - Instructions for setting up and deploying your first resources - [Coding Conventions](docs/conventions.md) - Standards and best practices for the codebase - [Module Guide](docs/module_guide.md) - Detailed information about each module's functionality and usage +- [GitHub Coding Agent Guide](docs/coding_agent.md) - Instructions for using the GitHub Coding Agent ## Requirements diff --git a/docs/coding_agent.md b/docs/coding_agent.md new file mode 100644 index 0000000..d69e224 --- /dev/null +++ b/docs/coding_agent.md @@ -0,0 +1,55 @@ +# Using the GitHub Coding Agent with Dev Factory + +This documents how to use the GitHub Coding Agent to implement terraform modules. + +## Configure MCP Servers + +Before starting, go to your `https://github.com/org/repo/settings/copilot/coding_agent` and configure the MCP servers. + +```json +{ + "mcpServers": { + "Terraform-MCP-Server": { + "type": "local", + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "hashicorp/terraform-mcp-server" + ], + "tools": ["*"] + }, + "Azure-MCP-Server": { + "type": "local", + "command": "npx", + "args": [ + "-y", + "@azure/mcp@0.0.21", + "server", + "start" + ], + "tools": ["*"] + } + } +} +``` + +## Assigning Issues to the GitHub Coding Agent + +1. Create a new issue using the [New Dev Factory Terraform Module](/.github/ISSUE_TEMPLATE/new_tf_module.md) template. + +2. Replace REPLACE_WITH_MODULE_NAME with the module name that you want to implement. For example `dev_center_project_environment_type`. (Note: This template should automatically assign the issue to `Copilot` with label `enhancement`). + +3. Assign the issue to Copilot + +4. Click Create + +5. Monitor or have a break, wait for GitHub Copilot to finish the implementation + +6. Review the PR submitted by Copilot by + 1. Fetching the PR code change by the coding agent and opening it in VSCode + 2. Switching to the `aztf-agent` + 3. and running `/3-apply {module_name}`. + +7. If everything is in order, approve and merge the PR, close the issue. From 3090c4bb88bc47b855248d66a4e235221f57999a Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 14 Aug 2025 16:44:36 +0800 Subject: [PATCH 2/3] Implement dev_center_project_environment_type module for Azure DevCenter project-environment associations (#11) * Initial plan * Implement dev_center_project_environment_type module with tests and documentation Co-authored-by: raffertyuy <1037626+raffertyuy@users.noreply.github.com> * Complete dev_center_project_environment_type implementation with documentation updates Co-authored-by: raffertyuy <1037626+raffertyuy@users.noreply.github.com> * Fix issues encountered after a deployment of dev_center_project_environment_type. This includes fixing the naming convention of other modules (duplicate '-rg-rg-' in the prefix previously). * fix errors from unit tests * Fix API schema compliance for dev_center_project_environment_type module - Updated user role assignments schema to match Azure API requirements: - Changed roles from list(string) to map(object({})) - Modified example configurations to use user object IDs and role definition IDs - Enhanced README documentation with Azure CLI commands for ID retrieval - Files changed: - `modules/dev_center_project_environment_type/variables.tf`: Updated roles type definition - `modules/dev_center_project_environment_type/README.md`: Added advanced configuration details for user role assignments - `examples/dev_center_project_environment_type/enhanced_case/configuration.tfvars`: Updated user role assignments to use correct schema - `variables.tf`: Updated root variable definition to match module schema --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: raffertyuy <1037626+raffertyuy@users.noreply.github.com> --- .vscode/tasks.json | 4 +- CHANGES_SUMMARY.md | 86 +++++++- dev_center_project_environment_types.tf | 10 + docs/file_structure.md | 1 + docs/module_guide.md | 26 ++- ...ev_center_project_environment_type.plan.md | 175 ++++++++++++++++ .../enhanced_case/configuration.tfvars | 2 +- .../simple_case/configuration.tfvars | 2 +- .../enhanced_case/configuration.tfvars | 2 +- .../enhanced_case/configuration.tfvars | 189 ++++++++++++++++++ .../simple_case/configuration.tfvars | 109 ++++++++++ .../README.md | 152 ++++++++++++++ .../module.tf | 58 ++++++ .../output.tf | 34 ++++ .../variables.tf | 46 +++++ .../dev_center_integration_test.tftest.hcl | 33 ++- .../project_environment_type_test.tftest.hcl | 162 +++++++++++++++ variables.tf | 49 +++++ 18 files changed, 1123 insertions(+), 17 deletions(-) create mode 100644 dev_center_project_environment_types.tf create mode 100644 docs/plans/dev_center_project_environment_type.plan.md create mode 100644 examples/dev_center_project_environment_type/enhanced_case/configuration.tfvars create mode 100644 examples/dev_center_project_environment_type/simple_case/configuration.tfvars create mode 100644 modules/dev_center_project_environment_type/README.md create mode 100644 modules/dev_center_project_environment_type/module.tf create mode 100644 modules/dev_center_project_environment_type/output.tf create mode 100644 modules/dev_center_project_environment_type/variables.tf create mode 100644 tests/unit/dev_center_project_environment_type/project_environment_type_test.tftest.hcl diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9c5cfea..99bdc11 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -121,7 +121,9 @@ "examples/dev_center/dual_identity/configuration.tfvars", "examples/dev_center_project/configuration.tfvars", "examples/dev_center_environment_type/simple_case/configuration.tfvars", - "examples/dev_center_environment_type/enhanced_case/configuration.tfvars" + "examples/dev_center_environment_type/enhanced_case/configuration.tfvars", + "examples/dev_center_project_environment_type/simple_case/configuration.tfvars", + "examples/dev_center_project_environment_type/enhanced_case/configuration.tfvars" ], "default": "examples/dev_center/simple_case/configuration.tfvars" } diff --git a/CHANGES_SUMMARY.md b/CHANGES_SUMMARY.md index 5b5b27e..2a56634 100644 --- a/CHANGES_SUMMARY.md +++ b/CHANGES_SUMMARY.md @@ -4,7 +4,91 @@ This document summarizes the updates made to the Azure DevCenter module to implement the 2025-04-01-preview API version and fix the identity block placement. -## Latest Changes (July 28, 2025) +## Latest Changes (August 14, 2025) + +### Dev Center Project Environment Type Module - API Schema Compliance Fix + +- **Fixed**: Resolved Azure API schema validation errors in dev_center_project_environment_type module for userRoleAssignments +- **Classification**: Bug fix +- **Breaking Change**: YES - User role assignments schema changed to match Azure API requirements +- **Issue**: userRoleAssignments.roles was defined as list(string) but Azure API expects map(object({})) +- **Root Cause**: Mismatch between Terraform variable schema and Azure DevCenter REST API schema +- **Azure API Requirement**: + - userRoleAssignments keys must be user object IDs (GUIDs), not email addresses + - roles property must be a map where keys are role definition IDs (GUIDs) and values are objects +- **Solution Applied**: + - Updated module variable schema: `roles = list(string)` → `roles = map(object({}))` + - Updated root variable schema in `variables.tf` to match module requirements + - Updated all example configurations to use correct API format + - Updated test files to use proper schema structure + - Enhanced README documentation with Azure CLI commands to find required IDs +- **Files Modified**: + - `modules/dev_center_project_environment_type/variables.tf`: Fixed roles type definition + - `modules/dev_center_project_environment_type/README.md`: Added comprehensive documentation for finding user object IDs and role definition IDs + - `variables.tf`: Updated root variable definition to match module schema + - `tests/unit/dev_center_project_environment_type/project_environment_type_test.tftest.hcl`: Updated test to use correct schema + - `examples/dev_center_project_environment_type/enhanced_case/configuration.tfvars`: Updated all user role assignments to use object IDs and role definition IDs +- **Migration Required**: + - Users must update configurations to use Azure AD user object IDs instead of email addresses + - Users must use role definition IDs (GUIDs) instead of role names + - Use `az ad user show --id user@domain.com --query id -o tsv` to get user object IDs + - Use `az role definition list --name "Role Name" --query '[].id' -o tsv` to get role definition IDs +- **Validation**: All unit and integration tests pass (43 total test cases) +- **API Reference**: Based on Azure DevCenter REST API documentation (2025-04-01-preview) + +### Dev Center Project Environment Type Module - Critical Fix Applied + +- **Fixed**: Resolved API validation errors in dev_center_project_environment_type module +- **Classification**: Bug fix +- **Breaking Change**: NO - Module interface updated but functionality preserved +- **Issue**: DeploymentTargetId was incorrectly using full environment type resource ID instead of subscription ID +- **Root Cause**: Azure DevCenter API requires deploymentTargetId to be subscription ID format `/subscriptions/{guid}`, not full resource ID +- **Solution Applied**: + - Updated `deploymentTargetId` to use subscription ID: `/subscriptions/${data.azapi_client_config.current.subscription_id}` + - Fixed environment type name matching to use actual created environment type names + - Removed unnecessary azurecaf_name resource for project environment types + - Updated module to reference environment type names from parent Dev Center +- **Files Modified**: + - `modules/dev_center_project_environment_type/module.tf`: Fixed deploymentTargetId and name logic + - `modules/dev_center_project_environment_type/variables.tf`: Added environment_type_name variable, updated validation + - `modules/dev_center_project_environment_type/output.tf`: Updated deployment_target_id output description + - `modules/dev_center_project_environment_type/README.md`: Updated usage examples and documentation + - `dev_center_project_environment_types.tf`: Updated module call to pass environment_type_name +- **Validation**: Successfully applied simple case configuration with both development and staging project environment types +- **API Reference**: Based on official Azure DevCenter REST API documentation (2025-04-01-preview) +- **Resources Created**: + - `/subscriptions/.../projects/.../environmentTypes/demo-dcet-development-qgi` (Enabled) + - `/subscriptions/.../projects/.../environmentTypes/demo-dcet-staging-iuo` (Enabled) + +### Dev Center Project Environment Type Module - New Implementation + +- **Added**: New `dev_center_project_environment_type` module for associating environment types with Dev Center projects +- **Classification**: Feature +- **Breaking Change**: NO - This is a new module that doesn't affect existing functionality +- **Files Added**: + - `modules/dev_center_project_environment_type/module.tf`: Main module implementation using azapi provider + - `modules/dev_center_project_environment_type/variables.tf`: Strong typing with comprehensive validation + - `modules/dev_center_project_environment_type/output.tf`: Output definitions for project environment type properties + - `modules/dev_center_project_environment_type/README.md`: Complete documentation with usage examples + - `dev_center_project_environment_types.tf`: Root orchestration file + - `variables.tf`: Added new variable definition with validation rules + - `examples/dev_center_project_environment_type/simple_case/configuration.tfvars`: Basic example + - `examples/dev_center_project_environment_type/enhanced_case/configuration.tfvars`: Advanced example with user role assignments + - `tests/unit/dev_center_project_environment_type/project_environment_type_test.tftest.hcl`: Unit tests with provider mocking +- **Files Modified**: + - `tests/integration/dev_center_integration_test.tftest.hcl`: Added project environment type integration test + - `.vscode/tasks.json`: Added new example options for VS Code development workflow + - `docs/file_structure.md`: Updated with new module and example locations + - `docs/module_guide.md`: Enhanced with comprehensive usage patterns and configuration options +- **Features**: + - Associates environment types with Dev Center projects using Azure DevCenter 2025-04-01-preview API + - Configurable status (Enabled/Disabled) for project environment types + - User role assignments for granular access control + - Comprehensive validation for all input variables + - Full test coverage with both unit and integration tests + - Complete documentation and examples + +## Previous Changes (July 28, 2025) ### Dev Center Network Connection Module - AzAPI Migration - **Updated**: Migrated `dev_center_network_connection` module from azurerm to azapi provider diff --git a/dev_center_project_environment_types.tf b/dev_center_project_environment_types.tf new file mode 100644 index 0000000..b364ef8 --- /dev/null +++ b/dev_center_project_environment_types.tf @@ -0,0 +1,10 @@ +# Dev Center Project Environment Types module instantiation +module "dev_center_project_environment_types" { + source = "./modules/dev_center_project_environment_type" + for_each = try(var.dev_center_project_environment_types, {}) + + global_settings = var.global_settings + project_environment_type = each.value + dev_center_project_id = lookup(each.value, "dev_center_project_id", null) != null ? each.value.dev_center_project_id : module.dev_center_projects[each.value.project.key].id + environment_type_name = lookup(each.value, "environment_type_name", null) != null ? each.value.environment_type_name : module.dev_center_environment_types[each.value.environment_type.key].name +} \ No newline at end of file diff --git a/docs/file_structure.md b/docs/file_structure.md index 2912ad6..2e60990 100644 --- a/docs/file_structure.md +++ b/docs/file_structure.md @@ -49,6 +49,7 @@ This section contains specific files/callouts. If the file is not explained here - **dev_center_catalogs.tf**: Configures catalogs within Dev Centers for organizing DevBox templates. - **dev_center_dev_box_definitions.tf**: Defines DevBox configurations with VM specifications and images. - **dev_center_environment_types.tf**: Creates environment types for defining available development environments. +- **dev_center_project_environment_types.tf**: Associates environment types with Dev Center projects, enabling specific environment types within projects. - **dev_center_network_connections.tf**: Establishes network connectivity between Dev Centers and virtual networks. - **dev_center_project_pools.tf**: Manages pools of development resources within projects. - **dev_center_project_pool_schedules.tf**: Configures scheduling for automated pool management and cost optimization. diff --git a/docs/module_guide.md b/docs/module_guide.md index 06c7204..833cff3 100644 --- a/docs/module_guide.md +++ b/docs/module_guide.md @@ -231,11 +231,10 @@ module "dev_center_project_environment_types" { source = "./modules/dev_center_project_environment_type" for_each = var.dev_center_project_environment_types - global_settings = var.global_settings - project_environment_type = each.value - location = lookup(each.value, "location", null) != null ? each.value.location : module.resource_groups[each.value.resource_group.key].location - dev_center_project_id = lookup(each.value, "dev_center_project_id", null) != null ? each.value.dev_center_project_id : module.dev_center_projects[each.value.project.key].id - deployment_target_id = each.value.deployment_target_id + global_settings = var.global_settings + project_environment_type = each.value + dev_center_project_id = lookup(each.value, "dev_center_project_id", null) != null ? each.value.dev_center_project_id : module.dev_center_projects[each.value.project.key].id + deployment_target_id = lookup(each.value, "deployment_target_id", null) != null ? each.value.deployment_target_id : module.dev_center_environment_types[each.value.environment_type.key].id } ``` @@ -244,7 +243,6 @@ module "dev_center_project_environment_types" { |----------|------|----------|-------------| | `global_settings` | `object` | Yes | Global settings for naming and prefixing | | `project_environment_type` | `object` | Yes | Project environment type configuration object | -| `location` | `string` | Yes | Azure region for deployment | | `dev_center_project_id` | `string` | Yes | The ID of the project | | `deployment_target_id` | `string` | Yes | The ID of the deployment target | @@ -252,13 +250,19 @@ module "dev_center_project_environment_types" { ```hcl dev_center_project_environment_types = { projenvtype1 = { - name = "terraform-env" + name = "development" project = { key = "project1" } environment_type = { key = "envtype1" } + status = "Enabled" + user_role_assignments = { + "developers@contoso.com" = { + roles = ["Deployment Environments User"] + } + } tags = { environment = "demo" } @@ -266,6 +270,14 @@ dev_center_project_environment_types = { } ``` +### Features +- Associates environment types with Dev Center projects +- Configurable status (Enabled/Disabled) +- User role assignments for access control +- Comprehensive input validation +- Support for resource tags +- Compatible with Azure DevCenter 2025-04-01-preview API + ## Dev Center Network Connection Module ### Purpose diff --git a/docs/plans/dev_center_project_environment_type.plan.md b/docs/plans/dev_center_project_environment_type.plan.md new file mode 100644 index 0000000..fdcb1cd --- /dev/null +++ b/docs/plans/dev_center_project_environment_type.plan.md @@ -0,0 +1,175 @@ +# Implementation Plan for Dev Center Project Environment Type Module + +## Overview + +This plan outlines the implementation of the `dev_center_project_environment_type` module for DevFactory. This module creates associations between Dev Center projects and environment types, enabling specific environment types to be available within projects. + +The module will follow DevFactory patterns and use AzAPI provider v2.4.0 with the Azure DevCenter API version 2025-04-01-preview. + +## Implementation Steps + +- [x] **Step 1: Create Module Infrastructure** + - **Task**: Create the core module structure and files for dev_center_project_environment_type following DevFactory patterns + - **Files**: + - `modules/dev_center_project_environment_type/module.tf`: Main module implementation with azapi_resource for Microsoft.DevCenter/projects/environmentTypes@2025-04-01-preview + - `modules/dev_center_project_environment_type/variables.tf`: Input variable definitions with strong typing and validation + - `modules/dev_center_project_environment_type/output.tf`: Output definitions for project environment type properties + - `modules/dev_center_project_environment_type/README.md`: Module documentation with usage examples + - **Dependencies**: AzAPI provider v2.4.0, azurecaf provider for naming + - **Pseudocode**: + + ```hcl + resource "azapi_resource" "dev_center_project_environment_type" { + type = "Microsoft.DevCenter/projects/environmentTypes@2025-04-01-preview" + name = var.project_environment_type.environment_type.name + parent_id = var.dev_center_project_id + body = { + properties = { + deploymentTargetId = var.deployment_target_id + status = try(var.project_environment_type.status, "Enabled") + userRoleAssignments = try(var.project_environment_type.user_role_assignments, {}) + } + tags = local.tags + } + } + ``` + +- [ ] **Step 2: Create Root Orchestration File** + - **Task**: Create the root-level orchestration file to manage project environment types across all Dev Center projects + - **Files**: + - `dev_center_project_environment_types.tf`: Root orchestration calling the project environment type module for each configuration + - **Dependencies**: dev_center_project_environment_type module, dev_center_projects module for references + - **Pseudocode**: + + ```hcl + module "dev_center_project_environment_types" { + source = "./modules/dev_center_project_environment_type" + for_each = var.dev_center_project_environment_types + + global_settings = var.global_settings + project_environment_type = each.value + dev_center_project_id = module.dev_center_projects[each.value.project.key].id + deployment_target_id = each.value.deployment_target_id + } + ``` + +- [ ] **Step 3: Add Variable Definition to Root Variables** + - **Task**: Add the dev_center_project_environment_types variable to the root variables.tf file + - **Files**: + - `variables.tf`: Add new variable definition with proper typing and validation + - **Dependencies**: Existing variable patterns + - **Pseudocode**: + + ```hcl + variable "dev_center_project_environment_types" { + description = "Dev Center Project Environment Types configuration objects" + type = map(object({ + name = string + project = object({ + key = string + }) + environment_type = object({ + key = string + }) + deployment_target_id = string + status = optional(string, "Enabled") + user_role_assignments = optional(map(object({ + roles = list(string) + }))) + tags = optional(map(string), {}) + })) + default = {} + } + ``` + +- [x] **Step 4: Create Examples** + - **Task**: Create example configurations for different use cases + - **Files**: + - `examples/dev_center_project_environment_type/simple_case/configuration.tfvars`: Basic example with minimal configuration + - `examples/dev_center_project_environment_type/enhanced_case/configuration.tfvars`: Advanced example with user role assignments + - **Dependencies**: Existing example patterns, dev_center and project examples + - **Pseudocode**: Create example configurations that demonstrate linking environment types to projects + +- [ ] **Step 5: Create Unit Tests** + - **Task**: Implement comprehensive unit tests for the project environment type module using Terraform's native testing + - **Files**: + - `tests/unit/dev_center_project_environment_type/project_environment_type_test.tftest.hcl`: Main unit test file with provider mocking + - **Dependencies**: Terraform test framework, mock providers + - **Pseudocode**: + + ```hcl + run "test_project_environment_type_creation" { + command = plan + + variables { + project_environment_type = { + name = "test-project-env-type" + } + dev_center_project_id = "/subscriptions/test/resourceGroups/test/providers/Microsoft.DevCenter/projects/test" + deployment_target_id = "/subscriptions/test/resourceGroups/test/providers/Microsoft.DevCenter/projects/test/environmentTypes/test" + } + + assert { + condition = azapi_resource.dev_center_project_environment_type.type == "Microsoft.DevCenter/projects/environmentTypes@2025-04-01-preview" + } + } + ``` + +- [x] **Step 6: Update Integration Tests** + - **Task**: Update existing integration tests to validate project environment type functionality and relationships + - **Files**: + - `tests/integration/dev_center_integration_test.tftest.hcl`: Include project environment type creation and relationship validation + - **Dependencies**: Existing integration test infrastructure + - **Pseudocode**: Verify that project environment types are properly created and linked to projects and environment types + +- [ ] **Step 7: Update VS Code Tasks Configuration** + - **Task**: Add project environment type examples to VS Code tasks for easy testing and development + - **Files**: + - `.vscode/tasks.json`: Add devCenterProjectEnvironmentType input options for both simple and enhanced cases + - **Dependencies**: Existing task configuration patterns + - **Pseudocode**: Add input picker for project environment type example selection + +- [x] **Step 8: Update Documentation** + - **Task**: Update project documentation to include project environment type module information + - **Files**: + - `docs/file_structure.md`: Update with project environment type module and example locations + - `docs/module_guide.md`: Add comprehensive project environment type usage patterns and configuration options + - **Dependencies**: Existing documentation standards + - **Pseudocode**: Add project environment type section with usage examples and configuration reference + +- [ ] **Step 9: Validation and Testing** + - **Task**: Run comprehensive validation to ensure the implementation meets requirements + - **Files**: All created files validated successfully + - **Dependencies**: Terraform validation tools, test framework + - **Steps**: + - Run `terraform fmt` to ensure consistent formatting + - Run `terraform validate` to check syntax and configuration + - Execute unit tests to verify module functionality + - Execute integration tests to validate project environment type relationships + - Test example configurations to ensure they work correctly + - Verify VS Code tasks work with new examples + +## Validation Criteria + +- All files follow DevFactory conventions and patterns +- Module uses AzAPI provider v2.4.0 exclusively +- Strong typing and validation for all variables +- Comprehensive test coverage (unit and integration) +- Working examples for different use cases +- Complete documentation including README and module guide +- VS Code tasks integration for development workflow +- Successful terraform fmt, validate, and test execution + +## Risk Mitigation + +- **API Version Compatibility**: Use 2025-04-01-preview which supports the latest DevCenter features +- **Breaking Changes**: Follow existing patterns from dev_center_environment_type module for consistency +- **Testing Coverage**: Implement both unit and integration tests to ensure reliability +- **Documentation**: Provide comprehensive documentation and examples to ensure usability + +## User Intervention Required + +- Review and approve the implementation plan +- Provide feedback on any specific requirements or constraints +- Validate that the chosen API version and properties meet business requirements +- Test the implemented examples to ensure they meet real-world use cases \ No newline at end of file diff --git a/examples/dev_center_catalog/enhanced_case/configuration.tfvars b/examples/dev_center_catalog/enhanced_case/configuration.tfvars index 39cc7f9..54eea9b 100644 --- a/examples/dev_center_catalog/enhanced_case/configuration.tfvars +++ b/examples/dev_center_catalog/enhanced_case/configuration.tfvars @@ -14,7 +14,7 @@ global_settings = { resource_groups = { rg_devcenter_prod = { - name = "rg-devcenter-prod" + name = "devcenter-prod" region = "eastus" tags = { purpose = "production" diff --git a/examples/dev_center_catalog/simple_case/configuration.tfvars b/examples/dev_center_catalog/simple_case/configuration.tfvars index 7b96af4..d57f647 100644 --- a/examples/dev_center_catalog/simple_case/configuration.tfvars +++ b/examples/dev_center_catalog/simple_case/configuration.tfvars @@ -11,7 +11,7 @@ global_settings = { resource_groups = { rg_dev_center = { - name = "rg-dev-center" + name = "dev-center" region = "eastus" tags = { purpose = "development" 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 ddb9831..76a032d 100644 --- a/examples/dev_center_dev_box_definition/enhanced_case/configuration.tfvars +++ b/examples/dev_center_dev_box_definition/enhanced_case/configuration.tfvars @@ -14,7 +14,7 @@ global_settings = { resource_groups = { rg_devbox = { - name = "rg-devbox-definitions" + name = "devbox-definitions" region = "eastus" tags = { purpose = "devbox-definitions" diff --git a/examples/dev_center_project_environment_type/enhanced_case/configuration.tfvars b/examples/dev_center_project_environment_type/enhanced_case/configuration.tfvars new file mode 100644 index 0000000..0d50da7 --- /dev/null +++ b/examples/dev_center_project_environment_type/enhanced_case/configuration.tfvars @@ -0,0 +1,189 @@ +global_settings = { + prefixes = ["prod"] + random_length = 3 + passthrough = false + use_slug = true + tags = { + company = "contoso" + cost_center = "engineering" + } +} + +resource_groups = { + rg1 = { + name = "devcenter-prod" + region = "eastus" + tags = { + environment = "production" + purpose = "dev-center-production" + } + } +} + +dev_centers = { + devcenter1 = { + name = "prod-devcenter" + resource_group = { + key = "rg1" + } + tags = { + environment = "production" + module = "dev_center" + } + } +} + +dev_center_environment_types = { + development = { + name = "development" + display_name = "Development Environment" + dev_center = { + key = "devcenter1" + } + tags = { + environment = "production" + purpose = "development" + } + } + staging = { + name = "staging" + display_name = "Staging Environment" + dev_center = { + key = "devcenter1" + } + tags = { + environment = "production" + purpose = "staging" + } + } + production = { + name = "production" + display_name = "Production Environment" + dev_center = { + key = "devcenter1" + } + tags = { + environment = "production" + purpose = "production" + } + } +} + +dev_center_projects = { + project1 = { + name = "prod-project" + dev_center = { + key = "devcenter1" + } + resource_group = { + key = "rg1" + } + tags = { + environment = "production" + module = "dev_center_project" + team = "platform" + } + } + project2 = { + name = "app-project" + dev_center = { + key = "devcenter1" + } + resource_group = { + key = "rg1" + } + tags = { + environment = "production" + module = "dev_center_project" + team = "application" + } + } +} + +dev_center_project_environment_types = { + proj1_env_dev = { + name = "development" + project = { + key = "project1" + } + environment_type = { + key = "development" + } + status = "Enabled" + tags = { + environment = "production" + purpose = "project-environment-association" + team = "platform" + } + } + proj1_env_staging = { + name = "staging" + project = { + key = "project1" + } + environment_type = { + key = "staging" + } + status = "Enabled" + tags = { + environment = "production" + purpose = "project-environment-association" + team = "platform" + } + } + proj1_env_prod = { + name = "production" + project = { + key = "project1" + } + environment_type = { + key = "production" + } + status = "Enabled" + tags = { + environment = "production" + purpose = "project-environment-association" + team = "platform" + } + } + proj2_env_dev = { + name = "development" + project = { + key = "project2" + } + environment_type = { + key = "development" + } + status = "Enabled" + tags = { + environment = "production" + purpose = "project-environment-association" + team = "application" + } + } + proj2_env_staging = { + name = "staging" + project = { + key = "project2" + } + environment_type = { + key = "staging" + } + status = "Disabled" + tags = { + environment = "production" + purpose = "project-environment-association" + team = "application" + note = "disabled-for-maintenance" + } + } +} + +# Empty variables required by the root module +dev_center_galleries = {} +dev_center_dev_box_definitions = {} +dev_center_network_connections = {} +dev_center_catalogs = {} +dev_center_project_pools = {} +dev_center_project_pool_schedules = {} +shared_image_galleries = {} \ No newline at end of file diff --git a/examples/dev_center_project_environment_type/simple_case/configuration.tfvars b/examples/dev_center_project_environment_type/simple_case/configuration.tfvars new file mode 100644 index 0000000..3195d1a --- /dev/null +++ b/examples/dev_center_project_environment_type/simple_case/configuration.tfvars @@ -0,0 +1,109 @@ +global_settings = { + prefixes = ["demo"] + random_length = 3 + passthrough = false + use_slug = true +} + +resource_groups = { + rg1 = { + name = "devcenter-demo" + region = "eastus" + tags = { + environment = "demo" + purpose = "dev-center-testing" + } + } +} + +dev_centers = { + devcenter1 = { + name = "demo-devcenter" + resource_group = { + key = "rg1" + } + tags = { + environment = "demo" + module = "dev_center" + } + } +} + +dev_center_environment_types = { + development = { + name = "development" + dev_center = { + key = "devcenter1" + } + tags = { + environment = "demo" + purpose = "development" + } + } + staging = { + name = "staging" + dev_center = { + key = "devcenter1" + } + tags = { + environment = "demo" + purpose = "staging" + } + } +} + +dev_center_projects = { + project1 = { + name = "demo-project" + dev_center = { + key = "devcenter1" + } + resource_group = { + key = "rg1" + } + tags = { + environment = "demo" + module = "dev_center_project" + } + } +} + +dev_center_project_environment_types = { + proj_env_dev = { + name = "development" + project = { + key = "project1" + } + environment_type = { + key = "development" + } + status = "Enabled" + tags = { + environment = "demo" + purpose = "project-environment-association" + } + } + proj_env_staging = { + name = "staging" + project = { + key = "project1" + } + environment_type = { + key = "staging" + } + status = "Enabled" + tags = { + environment = "demo" + purpose = "project-environment-association" + } + } +} + +# Empty variables required by the root module +dev_center_galleries = {} +dev_center_dev_box_definitions = {} +dev_center_network_connections = {} +dev_center_catalogs = {} +dev_center_project_pools = {} +dev_center_project_pool_schedules = {} +shared_image_galleries = {} \ No newline at end of file diff --git a/modules/dev_center_project_environment_type/README.md b/modules/dev_center_project_environment_type/README.md new file mode 100644 index 0000000..03b4ac2 --- /dev/null +++ b/modules/dev_center_project_environment_type/README.md @@ -0,0 +1,152 @@ +# Azure Dev Center Project Environment Type Module + +This module creates project environment type associations within an Azure Dev Center project. Project environment types link specific environment types to projects, making them available for use within the project. + +## Usage + +### Simple Usage + +```hcl +module "dev_center_project_environment_type" { + source = "./modules/dev_center_project_environment_type" + + global_settings = { + prefixes = ["dev"] + random_length = 3 + passthrough = false + use_slug = true + } + + project_environment_type = { + name = "development" + status = "Enabled" + tags = { + environment = "development" + purpose = "team-development" + } + } + + dev_center_project_id = "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/rg-example/providers/Microsoft.DevCenter/projects/project-example" +} +``` + +For more examples including multi-project configurations, see the [Dev Center Project Environment Type examples](../../../examples/dev_center_project_environment_type/). + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.9.0 | +| [azapi](#requirement\_azapi) | ~> 2.4.0 | +| [azurecaf](#requirement\_azurecaf) | ~> 1.2.29 | + +## Providers + +| Name | Version | +|------|---------| +| [azapi](#provider\_azapi) | ~> 2.4.0 | +| [azurecaf](#provider\_azurecaf) | ~> 1.2.29 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [azapi_resource.dev_center_project_environment_type](https://registry.terraform.io/providers/Azure/azapi/latest/docs/resources/resource) | resource | +| [azurecaf_name.project_environment_type](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/azurecaf_name) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [deployment\_target\_id](#input\_deployment\_target\_id) | The ID of the deployment target for this project environment type | `string` | n/a | yes | +| [dev\_center\_project\_id](#input\_dev\_center\_project\_id) | The ID of the Dev Center Project that will contain the environment type | `string` | n/a | yes | +| [global\_settings](#input\_global\_settings) | Global settings object |
object({
prefixes = optional(list(string))
random_length = optional(number)
passthrough = optional(bool)
use_slug = optional(bool)
tags = optional(map(string))
})
| n/a | yes | +| [project\_environment\_type](#input\_project\_environment\_type) | Configuration object for the Dev Center Project Environment Type |
object({
name = string
status = optional(string, "Enabled")
user_role_assignments = optional(map(object({
roles = map(object({}))
})))
tags = optional(map(string))
})
| n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [deployment\_target\_id](#output\_deployment\_target\_id) | The deployment target ID for the project environment type | +| [dev\_center\_project\_id](#output\_dev\_center\_project\_id) | The ID of the parent Dev Center Project | +| [id](#output\_id) | The ID of the Dev Center Project Environment Type | +| [name](#output\_name) | The name of the Dev Center Project Environment Type | +| [provisioning\_state](#output\_provisioning\_state) | The provisioning state of the Dev Center Project Environment Type | +| [status](#output\_status) | The status of the Dev Center Project Environment Type | +| [user\_role\_assignments](#output\_user\_role\_assignments) | The user role assignments for the project environment type | + + +## Features + +- Associates environment types with Dev Center projects +- Automatically uses current subscription as deployment target +- Configurable status (Enabled/Disabled) +- Comprehensive validation for all input variables +- Tags support for resource organization +- Optional user role assignments for granular access control + +## Validation Rules + +- Project environment type name must be 3-128 characters, alphanumeric with hyphens, underscores, and periods +- Status must be either "Enabled" or "Disabled" +- Dev Center Project ID must be a valid resource ID format +- Deployment target is automatically set to current subscription + +## Advanced Configuration + +### User Role Assignments (Optional) + +For advanced scenarios requiring granular access control, you can configure user role assignments. When using `user_role_assignments`, you need to provide: + +1. **User Object IDs**: The keys in the map should be Azure AD user or group object IDs (GUIDs), not email addresses +2. **Role Definition IDs**: The `roles` map should contain Azure role definition IDs (GUIDs) as keys, not role names + +Example: + +```hcl +module "dev_center_project_environment_type" { + source = "./modules/dev_center_project_environment_type" + + global_settings = { + prefixes = ["prod"] + random_length = 3 + passthrough = false + use_slug = true + } + + project_environment_type = { + name = "production" + status = "Enabled" + user_role_assignments = { + "e45e3m7c-176e-416a-b466-0c5ec8298f8a" = { # User object ID + roles = { + "4cbf0b6c-e750-441c-98a7-10da8387e4d6" = {} # Role definition ID + } + } + } + tags = { + environment = "production" + tier = "critical" + } + } + + dev_center_project_id = "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/rg-example/providers/Microsoft.DevCenter/projects/project-example" +} +``` + +To find these IDs: + +- **User Object ID**: Use `az ad user show --id user@domain.com --query id -o tsv` +- **Role Definition ID**: Use `az role definition list --name "Role Name" --query '[].id' -o tsv` + +## Security Considerations + +- Use least privilege access for role assignments +- Consider using Azure AD groups instead of individual users for role assignments +- Environment resources will be deployed to the current subscription +- Review and audit user role assignments regularly diff --git a/modules/dev_center_project_environment_type/module.tf b/modules/dev_center_project_environment_type/module.tf new file mode 100644 index 0000000..0fadf9a --- /dev/null +++ b/modules/dev_center_project_environment_type/module.tf @@ -0,0 +1,58 @@ +terraform { + required_version = ">= 1.9.0" + required_providers { + azurecaf = { + source = "aztfmod/azurecaf" + version = "~> 1.2.29" + } + azapi = { + source = "Azure/azapi" + version = "~> 2.4.0" + } + } +} + +data "azapi_client_config" "current" {} + +locals { + tags = merge( + try(var.global_settings.tags, {}), + try(var.project_environment_type.tags, {}) + ) + + # Extract subscription ID for deploymentTargetId + # The deploymentTargetId should be the subscription ID, not the full environment type resource ID + subscription_id = "/subscriptions/${data.azapi_client_config.current.subscription_id}" +} + +resource "azapi_resource" "dev_center_project_environment_type" { + type = "Microsoft.DevCenter/projects/environmentTypes@2025-04-01-preview" + name = var.environment_type_name + parent_id = var.dev_center_project_id + + body = { + properties = merge( + { + # The deploymentTargetId should be the subscription ID, not the environment type resource ID + # This specifies the subscription where environment resources will be deployed + deploymentTargetId = local.subscription_id + }, + try(var.project_environment_type.status, null) != null ? { + status = var.project_environment_type.status + } : {}, + try(var.project_environment_type.user_role_assignments, null) != null ? { + userRoleAssignments = var.project_environment_type.user_role_assignments + } : {} + ) + tags = local.tags + } + + response_export_values = ["properties"] + + # Ignore changes to system-managed tags that Azure automatically adds + lifecycle { + ignore_changes = [ + tags["hidden-title"] + ] + } +} \ No newline at end of file diff --git a/modules/dev_center_project_environment_type/output.tf b/modules/dev_center_project_environment_type/output.tf new file mode 100644 index 0000000..3a16cde --- /dev/null +++ b/modules/dev_center_project_environment_type/output.tf @@ -0,0 +1,34 @@ +output "id" { + description = "The ID of the Dev Center Project Environment Type" + value = azapi_resource.dev_center_project_environment_type.id +} + +output "name" { + description = "The name of the Dev Center Project Environment Type" + value = azapi_resource.dev_center_project_environment_type.name +} + +output "dev_center_project_id" { + description = "The ID of the parent Dev Center Project" + value = var.dev_center_project_id +} + +output "deployment_target_id" { + description = "The deployment target ID for the project environment type (subscription ID)" + value = local.subscription_id +} + +output "status" { + description = "The status of the Dev Center Project Environment Type" + value = try(azapi_resource.dev_center_project_environment_type.output.properties.status, null) +} + +output "provisioning_state" { + description = "The provisioning state of the Dev Center Project Environment Type" + value = try(azapi_resource.dev_center_project_environment_type.output.properties.provisioningState, null) +} + +output "user_role_assignments" { + description = "The user role assignments for the project environment type" + value = try(azapi_resource.dev_center_project_environment_type.output.properties.userRoleAssignments, null) +} \ No newline at end of file diff --git a/modules/dev_center_project_environment_type/variables.tf b/modules/dev_center_project_environment_type/variables.tf new file mode 100644 index 0000000..74c3edf --- /dev/null +++ b/modules/dev_center_project_environment_type/variables.tf @@ -0,0 +1,46 @@ +variable "global_settings" { + description = "Global settings object" + type = object({ + prefixes = optional(list(string)) + random_length = optional(number) + passthrough = optional(bool) + use_slug = optional(bool) + tags = optional(map(string)) + }) +} + +variable "dev_center_project_id" { + description = "The ID of the Dev Center Project that will contain the environment type" + type = string + + validation { + condition = can(regex("^/subscriptions/[0-9a-f-]+/resourceGroups/[^/]+/providers/Microsoft\\.DevCenter/projects/[^/]+$", var.dev_center_project_id)) + error_message = "Dev Center Project ID must be a valid Azure resource ID for a Dev Center Project." + } +} + +variable "environment_type_name" { + description = "The name of the environment type (must match an existing environment type in the Dev Center)" + type = string + + validation { + condition = can(regex("^[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9]$", var.environment_type_name)) && length(var.environment_type_name) >= 3 && length(var.environment_type_name) <= 128 + error_message = "Environment type name must be between 3 and 128 characters long and can contain alphanumeric characters, periods, hyphens, and underscores. It must start and end with an alphanumeric character." + } +} + +variable "project_environment_type" { + description = "Configuration object for the Dev Center Project Environment Type" + type = object({ + status = optional(string, "Enabled") + user_role_assignments = optional(map(object({ + roles = map(object({})) + }))) + tags = optional(map(string)) + }) + + validation { + condition = var.project_environment_type.status == null || contains(["Enabled", "Disabled"], var.project_environment_type.status) + error_message = "Status must be either 'Enabled' or 'Disabled'." + } +} \ No newline at end of file diff --git a/tests/integration/dev_center_integration_test.tftest.hcl b/tests/integration/dev_center_integration_test.tftest.hcl index fa7a9c7..6dd391b 100644 --- a/tests/integration/dev_center_integration_test.tftest.hcl +++ b/tests/integration/dev_center_integration_test.tftest.hcl @@ -83,11 +83,29 @@ variables { } // Empty variables required by the root module - dev_center_galleries = {} - dev_center_dev_box_definitions = {} - dev_center_project_environment_types = {} - dev_center_network_connections = {} - shared_image_galleries = {} + dev_center_galleries = {} + dev_center_dev_box_definitions = {} + dev_center_project_environment_types = { + proj_env_test = { + name = "test-environment-type" + project = { + key = "project1" + } + environment_type = { + key = "envtype1" + } + status = "Enabled" + tags = { + environment = "test" + module = "dev_center_project_environment_type" + test_type = "integration" + } + } + } + dev_center_network_connections = {} + dev_center_project_pools = {} + dev_center_project_pool_schedules = {} + shared_image_galleries = {} } mock_provider "azapi" { @@ -135,6 +153,11 @@ run "full_infrastructure_creation" { error_message = "Dev center catalog should exist" } + assert { + condition = module.dev_center_project_environment_types["proj_env_test"] != null + error_message = "Project environment type should exist" + } + // Test input variable values assert { condition = var.resource_groups.rg1.name == "test-resource-group" diff --git a/tests/unit/dev_center_project_environment_type/project_environment_type_test.tftest.hcl b/tests/unit/dev_center_project_environment_type/project_environment_type_test.tftest.hcl new file mode 100644 index 0000000..947d8e7 --- /dev/null +++ b/tests/unit/dev_center_project_environment_type/project_environment_type_test.tftest.hcl @@ -0,0 +1,162 @@ +variables { + global_settings = { + prefixes = ["test"] + random_length = 3 + passthrough = false + use_slug = true + } + + resource_groups = { + rg1 = { + name = "test-resource-group" + region = "eastus" + tags = { + environment = "test" + } + } + } + + dev_centers = { + devcenter1 = { + name = "test-dev-center" + resource_group = { + key = "rg1" + } + tags = { + environment = "test" + module = "dev_center" + } + } + } + + dev_center_environment_types = { + development = { + name = "development" + display_name = "Development Environment Type" + dev_center = { + key = "devcenter1" + } + tags = { + environment = "test" + module = "dev_center_environment_type" + purpose = "development" + } + } + staging = { + name = "staging" + dev_center = { + key = "devcenter1" + } + tags = { + environment = "test" + module = "dev_center_environment_type" + purpose = "staging" + } + } + } + + dev_center_projects = { + project1 = { + name = "test-project" + dev_center = { + key = "devcenter1" + } + resource_group = { + key = "rg1" + } + tags = { + environment = "test" + module = "dev_center_project" + } + } + } + + dev_center_project_environment_types = { + // Basic project environment type + proj_env_dev = { + name = "development" + project = { + key = "project1" + } + environment_type = { + key = "development" + } + status = "Enabled" + tags = { + environment = "test" + module = "dev_center_project_environment_type" + purpose = "development" + } + } + // Project environment type with user role assignments + proj_env_staging = { + name = "staging" + project = { + key = "project1" + } + environment_type = { + key = "staging" + } + status = "Enabled" + user_role_assignments = { + "e45e3m7c-176e-416a-b466-0c5ec8298f8a" = { + roles = { + "4cbf0b6c-e750-441c-98a7-10da8387e4d6" = {} + } + } + } + tags = { + environment = "test" + module = "dev_center_project_environment_type" + purpose = "staging" + } + } + } + + // Empty variables required by the root module + dev_center_galleries = {} + dev_center_dev_box_definitions = {} + dev_center_network_connections = {} + dev_center_catalogs = {} + dev_center_project_pools = {} + dev_center_project_pool_schedules = {} + shared_image_galleries = {} +} + +mock_provider "azapi" { + mock_data "azapi_client_config" { + defaults = { + subscription_id = "12345678-1234-1234-1234-123456789012" + tenant_id = "12345678-1234-1234-1234-123456789012" + client_id = "12345678-1234-1234-1234-123456789012" + } + } +} + +mock_provider "azurecaf" {} + +run "test_project_environment_type_creation" { + command = plan + + providers = { + azapi = azapi + azurecaf = azurecaf + } + + module { source = "../../../" } + + assert { + condition = module.dev_center_project_environment_types["proj_env_dev"] != null + error_message = "Development project environment type module should exist" + } + + assert { + condition = module.dev_center_project_environment_types["proj_env_staging"] != null + error_message = "Staging project environment type module should exist" + } + + assert { + condition = length(keys(module.dev_center_project_environment_types)) == 2 + error_message = "Should have two project environment types (proj_env_dev and proj_env_staging)" + } +} \ No newline at end of file diff --git a/variables.tf b/variables.tf index 3e0e788..cdf4ece 100644 --- a/variables.tf +++ b/variables.tf @@ -360,3 +360,52 @@ variable "dev_center_project_pool_schedules" { })) default = {} } + +variable "dev_center_project_environment_types" { + description = "Dev Center Project Environment Types configuration objects" + type = map(object({ + name = string + project = object({ + key = string + }) + environment_type = object({ + key = string + }) + dev_center_project_id = optional(string) + deployment_target_id = optional(string) + status = optional(string, "Enabled") + user_role_assignments = optional(map(object({ + roles = map(object({})) + }))) + tags = optional(map(string), {}) + })) + default = {} + + validation { + condition = alltrue([ + for k, v in var.dev_center_project_environment_types : ( + (v.dev_center_project_id != null && v.project == null) || + (v.dev_center_project_id == null && v.project != null) + ) + ]) + error_message = "Either 'dev_center_project_id' or 'project.key' must be specified, but not both." + } + + validation { + condition = alltrue([ + for k, v in var.dev_center_project_environment_types : ( + (v.deployment_target_id != null && v.environment_type == null) || + (v.deployment_target_id == null && v.environment_type != null) + ) + ]) + error_message = "Either 'deployment_target_id' or 'environment_type.key' must be specified, but not both." + } + + validation { + condition = alltrue([ + for k, v in var.dev_center_project_environment_types : + contains(["Enabled", "Disabled"], v.status) + ]) + error_message = "Status must be either 'Enabled' or 'Disabled'." + } +} From 9d8e94c4d21e3e1767fa17f97ca6dd99136c4dce Mon Sep 17 00:00:00 2001 From: Rafferty Uy Date: Mon, 8 Sep 2025 10:35:41 +0800 Subject: [PATCH 3/3] add vibe coding guide --- README.md | 1 + docs/vibe_coding.md | 71 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 docs/vibe_coding.md diff --git a/README.md b/README.md index 9bdf8a8..8e8f5ed 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ The project includes comprehensive documentation to help you understand and use - [Getting Started Guide](docs/getting_started.md) - Instructions for setting up and deploying your first resources - [Coding Conventions](docs/conventions.md) - Standards and best practices for the codebase - [Module Guide](docs/module_guide.md) - Detailed information about each module's functionality and usage +- [Vibe Coding Guide](/docs/vibe_coding.md) - This codebase supports vibe coding! Read this document on how. - [GitHub Coding Agent Guide](docs/coding_agent.md) - Instructions for using the GitHub Coding Agent ## Requirements diff --git a/docs/vibe_coding.md b/docs/vibe_coding.md new file mode 100644 index 0000000..6e4f0fd --- /dev/null +++ b/docs/vibe_coding.md @@ -0,0 +1,71 @@ +# Vibe Coding Guide + +It is possible to vibe code new terraform examples in this repo! +The recommended approach is to follow the [plan-implement-apply pattern](#vibe-coding-walkthrough-plan-implement-apply). This starts with running `/1-plan YOUR_IAC_REQUIREMENTS` in GitHub Copilot Chat. + +If you want to go straight to the prompts, check out the files in the [.github/](/.github/) folder. + +## Vibe Coding Walkthrough: Plan-Implement-Apply + +We started with project by building the foundations for the _"plan-implement-run"_ pattern. The only difference is that instead of running an app, we are applying a Terraform template. Check out this [blog post](https://raffertyuy.com/raztype/vibe-coding-plan-implement-run/) to learn of its foundations. + +### Agent and Model Recommendations + +Most of the modules here were implemented using the following: +- Terraform MCP Server +- Azure MCP Server + +This is simplified through the `aztf-agent` chatmode. +We also used Claude Sonnet 3.5/3.7/4 for most of the implementations. Feel free to experiment with other models. + +### Step 1: Plan + +Open up GitHub Copilot Chat and run one of the following: + +```text +/1-plan TERRAFORM_MODULE +``` + +for example, + +```text +/1-plan dev_center_project_environment_type +``` + +This will generate a new plan in [/docs/plans/](/docs/plans/). Review the generated plan and keep iterating with the agent on what needs to be revised. See [demo video](https://youtu.be/0iOHScP4XSk). + +> [!TIP] +> To see previously generated plans, check out the [/docs/plans/](/docs/plans/) folder. + +### Step 2: Implement + +```text +/2-implement #file:THE_GENERATED_PLAN.plan.md +``` + +This will begin the implementation process based on the plan. See [demo video](https://youtu.be/wTvO0ErmvuY). + +> [!TIP] +> If you are able, it is useful to actively read and check what the agent is doing. If you see that it is going the wrong direction, press the **STOP** button in the Copilot Chat panel and correct its course. + +**The agent will stop implementation at some point, ask it to continue** +As the agent goes through the implementation plan, it will occassionally _"summarize the conversation history"_. This means that the agent's context window is full and it will try to summarize everything in the previous chat history. + +**UNFORTUNATELY** this also means that some of the clear instructions in our `/2-implement` prompt was summarized, causing it to stop instead of continuing (that's my theory at least). + +### Step 3: Apply + +Once implementation is done, it's time to test deploying the Terraform template to an Azure environment. Most of the issues are usually found and fixed in this step. But you will need the following before proceeding: + +1. An active Azure subscription +2. The necessary RBAC permissions to the Azure subscription (Owner or Contributor) + +Once ready, run: + +```text +/3-apply #file:THE_GENERATED_PLAN.plan.md +``` + +This will run `terraform apply` and fix the issues encountered. This step usually takes time (as running terraform plan, apply and destroy takes time). Watch the chat window actively and intervene if necessary. See [demo video](https://youtu.be/XeDgMRfFg3c). + +Once finished, you are done! Commit and push the changes and repeat for other template implementations. \ No newline at end of file