diff --git a/.github/plugins/azure-kusto-graph-skills/hooks/scripts/track-telemetry.ps1 b/.github/plugins/azure-kusto-graph-skills/hooks/scripts/track-telemetry.ps1 index a7f514f4..c453b54b 100644 --- a/.github/plugins/azure-kusto-graph-skills/hooks/scripts/track-telemetry.ps1 +++ b/.github/plugins/azure-kusto-graph-skills/hooks/scripts/track-telemetry.ps1 @@ -74,8 +74,7 @@ # - toolArgs.path / toolArgs.filePath (Copilot CLI) # - tool_input.filePath / tool_input.file_path / tool_input.path (Claude Code / VS Code) # -# Recognized install paths (one set per plugin, see $pathPatterns below): -# azure-skills: +# Recognized azure-skills install paths: # - .copilot/installed-plugins//azure/skills/... # ( is the marketplace/catalog folder the plugin was # installed under, e.g. "awesome-copilot" — it does not necessarily @@ -83,11 +82,6 @@ # - .claude/plugins/cache/azure-skills/azure//skills/... # - .claude/plugins/cache/claude-plugins-official/azure//skills/... # - .vscode/agent-plugins/github.com/microsoft/azure-skills/.github/plugins/azure-skills/skills/... -# azure-kusto-graph-skills: -# - .copilot/installed-plugins//azure-kusto-graph-skills/skills/... -# - .claude/plugins/cache/azure-skills/azure-kusto-graph-skills//skills/... -# - .vscode/agent-plugins/github.com/microsoft/azure-skills/.github/plugins/azure-kusto-graph-skills/skills/... -# shared: # - .agents/skills/... # # If the path matches AND is not a SKILL.md file, the relative path after @@ -182,6 +176,20 @@ function Get-SkillVersion { return $null } +# Extract the plugin version from the top-level .plugin/plugin.json manifest. +# Returns $null if the file or expected JSON value cannot be read. +function Get-PluginVersion { + $pluginManifestPath = Join-Path (Split-Path -Parent $skillsDir) '.plugin/plugin.json' + if (-not (Test-Path -LiteralPath $pluginManifestPath)) { return $null } + try { + $manifest = Get-Content -LiteralPath $pluginManifestPath -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop + if ($manifest.version -is [string] -and -not [string]::IsNullOrWhiteSpace($manifest.version)) { + return $manifest.version + } + } catch { } + return $null +} + # === Main Processing === # Read entire stdin at once - hooks send one complete JSON per invocation @@ -281,32 +289,14 @@ function Get-ToolInputPath { # === STEP 2: Determine what to track for azmcp === -# Path patterns per client, one block per plugin (used for SKILL.md and -# file-reference matching). Add a new block (with the "azure-skills" -# segments swapped for the new plugin's name) when onboarding another plugin. - -# --- azure-skills plugin --- -# The Copilot CLI pattern wildcards the catalog/marketplace folder name -# (e.g. "awesome-copilot") since it does not necessarily match the plugin's -# own name ("azure"). +# Azure-skills path patterns per client (used for SKILL.md and file-reference matching) $pathPatternCopilot = '\.copilot/installed-plugins/[^/]+/azure/skills/' $pathPatternClaude = '\.claude/plugins/cache/(azure-skills|claude-plugins-official)/azure/[0-9.]+/skills/' $pathPatternVscodeAgentPlugins = 'agent-plugins/github\.com/microsoft/azure-skills/\.github/plugins/azure-skills/skills/' - -# --- azure-kusto-graph-skills plugin --- -$pathPatternCopilotKustoGraph = '\.copilot/installed-plugins/[^/]+/azure-kusto-graph-skills/skills/' -$pathPatternClaudeKustoGraph = '\.claude/plugins/cache/azure-skills/azure-kusto-graph-skills/[0-9.]+/skills/' -$pathPatternVscodeAgentPluginsKustoGraph = 'agent-plugins/github\.com/microsoft/azure-skills/\.github/plugins/azure-kusto-graph-skills/skills/' - -# --- shared across all plugins --- $pathPatternAgentsSkills = '\.agents/skills/' # Put the path patterns into an array for easier iteration -$pathPatterns = @( - $pathPatternCopilot, $pathPatternClaude, $pathPatternVscodeAgentPlugins, - $pathPatternCopilotKustoGraph, $pathPatternClaudeKustoGraph, $pathPatternVscodeAgentPluginsKustoGraph, - $pathPatternAgentsSkills -) +$pathPatterns = @($pathPatternCopilot, $pathPatternClaude, $pathPatternVscodeAgentPlugins, $pathPatternAgentsSkills) # If $env:AZURE_SKILLS_PLUGIN_ROOT is set, add it to the path patterns for local skill development if ($env:AZURE_SKILLS_PLUGIN_ROOT) { @@ -417,6 +407,8 @@ if (-not $filePath -and -not $skillName) { # === STEP 3: Publish event === if ($shouldTrack) { + $pluginVersion = Get-PluginVersion + # Build MCP command arguments $mcpArgs = @( "server", "plugin-telemetry", @@ -428,6 +420,7 @@ if ($shouldTrack) { if ($sessionId) { $mcpArgs += "--session-id"; $mcpArgs += $sessionId } if ($skillName) { $mcpArgs += "--skill-name"; $mcpArgs += $skillName } if ($skillVersion) { $mcpArgs += "--skill-version"; $mcpArgs += $skillVersion } + if ($pluginVersion) { $mcpArgs += "--plugin-version"; $mcpArgs += $pluginVersion } if ($azureToolName) { $mcpArgs += "--tool-name"; $mcpArgs += $azureToolName } # Convert forward slashes to backslashes for azmcp allowlist compatibility if ($filePath) { $mcpArgs += "--file-reference"; $mcpArgs += ($filePath -replace '/', '\') } diff --git a/.github/plugins/azure-kusto-graph-skills/hooks/scripts/track-telemetry.sh b/.github/plugins/azure-kusto-graph-skills/hooks/scripts/track-telemetry.sh index 1eba994c..a3c85ceb 100755 --- a/.github/plugins/azure-kusto-graph-skills/hooks/scripts/track-telemetry.sh +++ b/.github/plugins/azure-kusto-graph-skills/hooks/scripts/track-telemetry.sh @@ -76,8 +76,7 @@ # - toolArgs.path / toolArgs.filePath (Copilot CLI) # - tool_input.filePath / tool_input.file_path / tool_input.path (Claude Code / VS Code) # -# Recognized install paths (one set per plugin, see is_azure_skills_path): -# azure-skills: +# Recognized azure-skills install paths: # - .copilot/installed-plugins//azure/skills/... # ( is the marketplace/catalog folder the plugin was # installed under, e.g. "awesome-copilot" — it does not necessarily @@ -85,11 +84,6 @@ # - .claude/plugins/cache/azure-skills/azure//skills/... # - .claude/plugins/cache/claude-plugins-official/azure//skills/... # - .vscode/agent-plugins/github.com/microsoft/azure-skills/.github/plugins/azure-skills/skills/... -# azure-kusto-graph-skills: -# - .copilot/installed-plugins//azure-kusto-graph-skills/skills/... -# - .claude/plugins/cache/azure-skills/azure-kusto-graph-skills//skills/... -# - .vscode/agent-plugins/github.com/microsoft/azure-skills/.github/plugins/azure-kusto-graph-skills/skills/... -# shared: # - .agents/skills/... # # If the path matches AND is not a SKILL.md file, the relative path after @@ -164,6 +158,20 @@ get_skill_version() { | sed -E 's/^[[:space:]]*version:[[:space:]]*//; s/^["'"'"']//; s/["'"'"'][[:space:]]*$//; s/[[:space:]]*$//' } +# Extract the plugin version from the top-level .plugin/plugin.json manifest. +# Prints nothing if the file or expected JSON value cannot be read. +get_plugin_version() { + local pluginManifestPath + pluginManifestPath="$(dirname "$SKILLS_DIR")/.plugin/plugin.json" + [ -f "$pluginManifestPath" ] || return 0 + node -e ' + try { + const manifest = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")); + if (typeof manifest.version === "string" && manifest.version) process.stdout.write(manifest.version); + } catch { } + ' "$pluginManifestPath" 2>/dev/null +} + # === JSON Parsing Functions (using sed - portable across platforms) === # Extract simple string field from JSON @@ -288,30 +296,15 @@ fi # === STEP 2: Determine what to track for azmcp === -# Check if a path matches any known plugin skills folder structure. -# Each plugin has its own block below — add a new block (with the -# "azure-skills" segments swapped for the new plugin's name) when onboarding -# another plugin. Returns 0 (true) if matched, 1 (false) otherwise. +# Check if a path matches any known azure-skills folder structure +# Returns 0 (true) if matched, 1 (false) otherwise is_azure_skills_path() { local p="$1" - - # --- azure-skills plugin --- - # The Copilot CLI pattern wildcards the catalog/marketplace folder name - # (e.g. "awesome-copilot") since it does not necessarily match the - # plugin's own name ("azure"). [[ "$p" == *".copilot/installed-plugins/"*"/azure/skills/"* ]] && return 0 [[ "$p" == *".claude/plugins/cache/azure-skills/azure/"*"/skills/"* ]] && return 0 [[ "$p" == *".claude/plugins/cache/claude-plugins-official/azure/"*"/skills/"* ]] && return 0 [[ "$p" == *"agent-plugins/github.com/microsoft/azure-skills/.github/plugins/azure-skills/skills/"* ]] && return 0 - - # --- azure-kusto-graph-skills plugin --- - [[ "$p" == *".copilot/installed-plugins/"*"/azure-kusto-graph-skills/skills/"* ]] && return 0 - [[ "$p" == *".claude/plugins/cache/azure-skills/azure-kusto-graph-skills/"*"/skills/"* ]] && return 0 - [[ "$p" == *"agent-plugins/github.com/microsoft/azure-skills/.github/plugins/azure-kusto-graph-skills/skills/"* ]] && return 0 - - # --- shared across all plugins --- [[ "$p" == *".agents/skills/"* ]] && return 0 - # Local plugin development: match paths under AZURE_SKILLS_PLUGIN_ROOT/skills/ # (e.g. when loading a local plugin via `--plugin-dir`) if [ -n "$AZURE_SKILLS_PLUGIN_ROOT" ]; then @@ -410,6 +403,8 @@ fi # === STEP 3: Publish event via azmcp === if [ "$shouldTrack" = true ]; then + pluginVersion=$(get_plugin_version) + # Build MCP command arguments (using array for proper quoting) mcpArgs=( "server" "plugin-telemetry" @@ -421,6 +416,7 @@ if [ "$shouldTrack" = true ]; then [ -n "$sessionId" ] && mcpArgs+=("--session-id" "$sessionId") [ -n "$skillName" ] && mcpArgs+=("--skill-name" "$skillName") [ -n "$skillVersion" ] && mcpArgs+=("--skill-version" "$skillVersion") + [ -n "$pluginVersion" ] && mcpArgs+=("--plugin-version" "$pluginVersion") [ -n "$azureToolName" ] && mcpArgs+=("--tool-name" "$azureToolName") # Convert forward slashes to backslashes for azmcp allowlist compatibility [ -n "$filePath" ] && mcpArgs+=("--file-reference" "$(echo "$filePath" | tr '/' '\\')") @@ -435,4 +431,3 @@ fi # Output success to stdout (required by hooks) return_success - diff --git a/.github/plugins/azure-skills/.claude-plugin/plugin.json b/.github/plugins/azure-skills/.claude-plugin/plugin.json index 3b270c2b..446848f4 100644 --- a/.github/plugins/azure-skills/.claude-plugin/plugin.json +++ b/.github/plugins/azure-skills/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "azure", "description": "Microsoft Azure MCP and Skills integration for cloud resource management, deployments, and Azure services. Manage your Azure infrastructure, monitor applications, and deploy resources directly from Claude Code.", - "version": "1.2.33", + "version": "1.2.34", "author": { "name": "Microsoft", "url": "https://www.microsoft.com" diff --git a/.github/plugins/azure-skills/.cursor-plugin/plugin.json b/.github/plugins/azure-skills/.cursor-plugin/plugin.json index 2fe30b05..3807f1e0 100644 --- a/.github/plugins/azure-skills/.cursor-plugin/plugin.json +++ b/.github/plugins/azure-skills/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "azure", "description": "Microsoft Azure MCP and Skills integration for cloud resource management, deployments, and Azure services. Manage your Azure infrastructure, monitor applications, and deploy resources directly from Cursor.", - "version": "1.2.33", + "version": "1.2.34", "author": { "name": "Microsoft", "url": "https://www.microsoft.com" diff --git a/.github/plugins/azure-skills/.plugin/plugin.json b/.github/plugins/azure-skills/.plugin/plugin.json index 36efa8b0..dfc3a2fb 100644 --- a/.github/plugins/azure-skills/.plugin/plugin.json +++ b/.github/plugins/azure-skills/.plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "azure", "description": "Microsoft Azure MCP and Skills integration for cloud resource management, deployments, and Azure services. Manage your Azure infrastructure, monitor applications, and deploy resources directly from your development environment.", - "version": "1.2.33", + "version": "1.2.34", "author": { "name": "Microsoft", "url": "https://www.microsoft.com" diff --git a/.github/plugins/azure-skills/CHANGELOG.md b/.github/plugins/azure-skills/CHANGELOG.md index 50a0da53..26c26475 100644 --- a/.github/plugins/azure-skills/CHANGELOG.md +++ b/.github/plugins/azure-skills/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 1.2.34 + +- feat: Added Referenced Workloads Updates to Azure Enterprise Infra Planner Skills (feature: Referenced Workloads) ([#3094](https://github.com/microsoft/GitHub-Copilot-for-Azure/pull/3094)) + ## 1.2.33 - refactor: remove duplicate content from Foundry skill description ([#3104](https://github.com/microsoft/GitHub-Copilot-for-Azure/pull/3104)) diff --git a/.github/plugins/azure-skills/hooks/scripts/track-telemetry.ps1 b/.github/plugins/azure-skills/hooks/scripts/track-telemetry.ps1 index a7f514f4..c453b54b 100644 --- a/.github/plugins/azure-skills/hooks/scripts/track-telemetry.ps1 +++ b/.github/plugins/azure-skills/hooks/scripts/track-telemetry.ps1 @@ -74,8 +74,7 @@ # - toolArgs.path / toolArgs.filePath (Copilot CLI) # - tool_input.filePath / tool_input.file_path / tool_input.path (Claude Code / VS Code) # -# Recognized install paths (one set per plugin, see $pathPatterns below): -# azure-skills: +# Recognized azure-skills install paths: # - .copilot/installed-plugins//azure/skills/... # ( is the marketplace/catalog folder the plugin was # installed under, e.g. "awesome-copilot" — it does not necessarily @@ -83,11 +82,6 @@ # - .claude/plugins/cache/azure-skills/azure//skills/... # - .claude/plugins/cache/claude-plugins-official/azure//skills/... # - .vscode/agent-plugins/github.com/microsoft/azure-skills/.github/plugins/azure-skills/skills/... -# azure-kusto-graph-skills: -# - .copilot/installed-plugins//azure-kusto-graph-skills/skills/... -# - .claude/plugins/cache/azure-skills/azure-kusto-graph-skills//skills/... -# - .vscode/agent-plugins/github.com/microsoft/azure-skills/.github/plugins/azure-kusto-graph-skills/skills/... -# shared: # - .agents/skills/... # # If the path matches AND is not a SKILL.md file, the relative path after @@ -182,6 +176,20 @@ function Get-SkillVersion { return $null } +# Extract the plugin version from the top-level .plugin/plugin.json manifest. +# Returns $null if the file or expected JSON value cannot be read. +function Get-PluginVersion { + $pluginManifestPath = Join-Path (Split-Path -Parent $skillsDir) '.plugin/plugin.json' + if (-not (Test-Path -LiteralPath $pluginManifestPath)) { return $null } + try { + $manifest = Get-Content -LiteralPath $pluginManifestPath -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop + if ($manifest.version -is [string] -and -not [string]::IsNullOrWhiteSpace($manifest.version)) { + return $manifest.version + } + } catch { } + return $null +} + # === Main Processing === # Read entire stdin at once - hooks send one complete JSON per invocation @@ -281,32 +289,14 @@ function Get-ToolInputPath { # === STEP 2: Determine what to track for azmcp === -# Path patterns per client, one block per plugin (used for SKILL.md and -# file-reference matching). Add a new block (with the "azure-skills" -# segments swapped for the new plugin's name) when onboarding another plugin. - -# --- azure-skills plugin --- -# The Copilot CLI pattern wildcards the catalog/marketplace folder name -# (e.g. "awesome-copilot") since it does not necessarily match the plugin's -# own name ("azure"). +# Azure-skills path patterns per client (used for SKILL.md and file-reference matching) $pathPatternCopilot = '\.copilot/installed-plugins/[^/]+/azure/skills/' $pathPatternClaude = '\.claude/plugins/cache/(azure-skills|claude-plugins-official)/azure/[0-9.]+/skills/' $pathPatternVscodeAgentPlugins = 'agent-plugins/github\.com/microsoft/azure-skills/\.github/plugins/azure-skills/skills/' - -# --- azure-kusto-graph-skills plugin --- -$pathPatternCopilotKustoGraph = '\.copilot/installed-plugins/[^/]+/azure-kusto-graph-skills/skills/' -$pathPatternClaudeKustoGraph = '\.claude/plugins/cache/azure-skills/azure-kusto-graph-skills/[0-9.]+/skills/' -$pathPatternVscodeAgentPluginsKustoGraph = 'agent-plugins/github\.com/microsoft/azure-skills/\.github/plugins/azure-kusto-graph-skills/skills/' - -# --- shared across all plugins --- $pathPatternAgentsSkills = '\.agents/skills/' # Put the path patterns into an array for easier iteration -$pathPatterns = @( - $pathPatternCopilot, $pathPatternClaude, $pathPatternVscodeAgentPlugins, - $pathPatternCopilotKustoGraph, $pathPatternClaudeKustoGraph, $pathPatternVscodeAgentPluginsKustoGraph, - $pathPatternAgentsSkills -) +$pathPatterns = @($pathPatternCopilot, $pathPatternClaude, $pathPatternVscodeAgentPlugins, $pathPatternAgentsSkills) # If $env:AZURE_SKILLS_PLUGIN_ROOT is set, add it to the path patterns for local skill development if ($env:AZURE_SKILLS_PLUGIN_ROOT) { @@ -417,6 +407,8 @@ if (-not $filePath -and -not $skillName) { # === STEP 3: Publish event === if ($shouldTrack) { + $pluginVersion = Get-PluginVersion + # Build MCP command arguments $mcpArgs = @( "server", "plugin-telemetry", @@ -428,6 +420,7 @@ if ($shouldTrack) { if ($sessionId) { $mcpArgs += "--session-id"; $mcpArgs += $sessionId } if ($skillName) { $mcpArgs += "--skill-name"; $mcpArgs += $skillName } if ($skillVersion) { $mcpArgs += "--skill-version"; $mcpArgs += $skillVersion } + if ($pluginVersion) { $mcpArgs += "--plugin-version"; $mcpArgs += $pluginVersion } if ($azureToolName) { $mcpArgs += "--tool-name"; $mcpArgs += $azureToolName } # Convert forward slashes to backslashes for azmcp allowlist compatibility if ($filePath) { $mcpArgs += "--file-reference"; $mcpArgs += ($filePath -replace '/', '\') } diff --git a/.github/plugins/azure-skills/hooks/scripts/track-telemetry.sh b/.github/plugins/azure-skills/hooks/scripts/track-telemetry.sh index 1eba994c..a3c85ceb 100755 --- a/.github/plugins/azure-skills/hooks/scripts/track-telemetry.sh +++ b/.github/plugins/azure-skills/hooks/scripts/track-telemetry.sh @@ -76,8 +76,7 @@ # - toolArgs.path / toolArgs.filePath (Copilot CLI) # - tool_input.filePath / tool_input.file_path / tool_input.path (Claude Code / VS Code) # -# Recognized install paths (one set per plugin, see is_azure_skills_path): -# azure-skills: +# Recognized azure-skills install paths: # - .copilot/installed-plugins//azure/skills/... # ( is the marketplace/catalog folder the plugin was # installed under, e.g. "awesome-copilot" — it does not necessarily @@ -85,11 +84,6 @@ # - .claude/plugins/cache/azure-skills/azure//skills/... # - .claude/plugins/cache/claude-plugins-official/azure//skills/... # - .vscode/agent-plugins/github.com/microsoft/azure-skills/.github/plugins/azure-skills/skills/... -# azure-kusto-graph-skills: -# - .copilot/installed-plugins//azure-kusto-graph-skills/skills/... -# - .claude/plugins/cache/azure-skills/azure-kusto-graph-skills//skills/... -# - .vscode/agent-plugins/github.com/microsoft/azure-skills/.github/plugins/azure-kusto-graph-skills/skills/... -# shared: # - .agents/skills/... # # If the path matches AND is not a SKILL.md file, the relative path after @@ -164,6 +158,20 @@ get_skill_version() { | sed -E 's/^[[:space:]]*version:[[:space:]]*//; s/^["'"'"']//; s/["'"'"'][[:space:]]*$//; s/[[:space:]]*$//' } +# Extract the plugin version from the top-level .plugin/plugin.json manifest. +# Prints nothing if the file or expected JSON value cannot be read. +get_plugin_version() { + local pluginManifestPath + pluginManifestPath="$(dirname "$SKILLS_DIR")/.plugin/plugin.json" + [ -f "$pluginManifestPath" ] || return 0 + node -e ' + try { + const manifest = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")); + if (typeof manifest.version === "string" && manifest.version) process.stdout.write(manifest.version); + } catch { } + ' "$pluginManifestPath" 2>/dev/null +} + # === JSON Parsing Functions (using sed - portable across platforms) === # Extract simple string field from JSON @@ -288,30 +296,15 @@ fi # === STEP 2: Determine what to track for azmcp === -# Check if a path matches any known plugin skills folder structure. -# Each plugin has its own block below — add a new block (with the -# "azure-skills" segments swapped for the new plugin's name) when onboarding -# another plugin. Returns 0 (true) if matched, 1 (false) otherwise. +# Check if a path matches any known azure-skills folder structure +# Returns 0 (true) if matched, 1 (false) otherwise is_azure_skills_path() { local p="$1" - - # --- azure-skills plugin --- - # The Copilot CLI pattern wildcards the catalog/marketplace folder name - # (e.g. "awesome-copilot") since it does not necessarily match the - # plugin's own name ("azure"). [[ "$p" == *".copilot/installed-plugins/"*"/azure/skills/"* ]] && return 0 [[ "$p" == *".claude/plugins/cache/azure-skills/azure/"*"/skills/"* ]] && return 0 [[ "$p" == *".claude/plugins/cache/claude-plugins-official/azure/"*"/skills/"* ]] && return 0 [[ "$p" == *"agent-plugins/github.com/microsoft/azure-skills/.github/plugins/azure-skills/skills/"* ]] && return 0 - - # --- azure-kusto-graph-skills plugin --- - [[ "$p" == *".copilot/installed-plugins/"*"/azure-kusto-graph-skills/skills/"* ]] && return 0 - [[ "$p" == *".claude/plugins/cache/azure-skills/azure-kusto-graph-skills/"*"/skills/"* ]] && return 0 - [[ "$p" == *"agent-plugins/github.com/microsoft/azure-skills/.github/plugins/azure-kusto-graph-skills/skills/"* ]] && return 0 - - # --- shared across all plugins --- [[ "$p" == *".agents/skills/"* ]] && return 0 - # Local plugin development: match paths under AZURE_SKILLS_PLUGIN_ROOT/skills/ # (e.g. when loading a local plugin via `--plugin-dir`) if [ -n "$AZURE_SKILLS_PLUGIN_ROOT" ]; then @@ -410,6 +403,8 @@ fi # === STEP 3: Publish event via azmcp === if [ "$shouldTrack" = true ]; then + pluginVersion=$(get_plugin_version) + # Build MCP command arguments (using array for proper quoting) mcpArgs=( "server" "plugin-telemetry" @@ -421,6 +416,7 @@ if [ "$shouldTrack" = true ]; then [ -n "$sessionId" ] && mcpArgs+=("--session-id" "$sessionId") [ -n "$skillName" ] && mcpArgs+=("--skill-name" "$skillName") [ -n "$skillVersion" ] && mcpArgs+=("--skill-version" "$skillVersion") + [ -n "$pluginVersion" ] && mcpArgs+=("--plugin-version" "$pluginVersion") [ -n "$azureToolName" ] && mcpArgs+=("--tool-name" "$azureToolName") # Convert forward slashes to backslashes for azmcp allowlist compatibility [ -n "$filePath" ] && mcpArgs+=("--file-reference" "$(echo "$filePath" | tr '/' '\\')") @@ -435,4 +431,3 @@ fi # Output success to stdout (required by hooks) return_success - diff --git a/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/SKILL.md b/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/SKILL.md index 1babce75..eccc653c 100644 --- a/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/SKILL.md +++ b/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/SKILL.md @@ -4,7 +4,7 @@ description: "Architect and provision enterprise Azure infrastructure from workl license: MIT metadata: author: Microsoft - version: "1.3.1" + version: "1.4.1" --- # Azure Enterprise Infra Planner @@ -24,7 +24,7 @@ Activate this skill when user wants to: | Property | Details | |---|---| | MCP tools | `insights_get`, `get_azure_bestpractices_get`, `wellarchitectedframework_serviceguide_get`, `microsoft_docs_fetch`, `microsoft_docs_search`, `bicepschema_get` | -| CLI commands | `az deployment group create`, `az bicep build`, `az resource list`, `terraform init`, `terraform plan`, `terraform validate`, `terraform apply` | +| CLI commands | `az deployment group create`, `az bicep build`, `az resource list`, `terraform init`, `terraform plan`, `terraform validate`, `terraform apply`, `checkov` | | Output schema | [schema.md](references/schema.md) | | Key references | [workflow.md](references/workflow.md), [waf-checklist.md](references/waf-checklist.md), [resources/](references/resources/README.md), [constraints/](references/constraints/README.md) | @@ -32,6 +32,58 @@ Activate this skill when user wants to: Follow the step-by-step instructions in [workflow.md](references/workflow.md) to execute the 7 phases of infrastructure planning and provisioning. +## Architecture + +The skill runs a **7-phase, gated pipeline**. Input is triaged into one of two flows: + +- **Greenfield** — only new requirements; run the phases straight through. +- **Referenced (brownfield)** — the user supplies something that already exists (a live resource / + resource group / subscription, IaC or an infra plan, or a requirements doc). The same phases run, plus + [referenced-workload.md](references/referenced-workload.md): existing resources are inventoried and + referenced (never recreated), the new workload is wired into them, and **Phase 7 deploys additively** + (incremental only — never modifying or destroying the referenced resources). + +Every phase advances only after its gate passes. Phase 5 requires explicit user approval; **Phase 6 is a +hardened, self-verifying gate** — the generated IaC must be secure-by-default, pass local validation +(`az bicep build` / `terraform validate`) with zero errors, pass a `checkov` security scan with no +unresolved high/critical findings, and the skill must **show the command output** and emit a completion +self-check before advancing; Phase 7 requires an explicit, risk-acknowledged deploy confirmation. + +```mermaid +flowchart TD + IN([Input]) --> TRIAGE{Existing infra
referenced?} + TRIAGE -- "No (greenfield)" --> P1 + TRIAGE -- "Yes (referenced)" --> RW[/referenced-workload.md:
inventory + assign roles
reference, never recreate/] + RW --> P1 + + subgraph PIPE [7-phase gated pipeline] + direction TB + P1[Phase 1 · Extract insights] --> P2[Phase 2 · Research best practices] + P2 --> P3[Phase 3 · Research resources] + P3 --> P4[Phase 4 · Generate plan] + P4 --> P5{Phase 5 · Verify
user approves?} + P5 -- "no" --> P4 + P5 -- "approved" --> P6[Phase 6 · Generate IaC] + P6 --> VAL{Validate
az bicep build /
terraform validate} + VAL -- "errors" --> P6 + VAL -- "clean" --> P7{Phase 7 · Deploy
risk-ack confirm?} + end + + P7 -- "greenfield" --> DEP[az deployment / terraform apply] + P7 -- "referenced" --> DEPADD[Additive deploy · incremental only
what-if preview · no destroy of
referenced resources] + DEP --> OUT([Deployed]) + DEPADD --> OUT + + classDef gate fill:#fff3cd,stroke:#d39e00,color:#000; + classDef ref fill:#e2f0d9,stroke:#548235,color:#000; + class P5,VAL,P7,TRIAGE gate; + class RW,DEPADD ref; +``` + +**Artifacts** (written under `/`): `.azure/insights.json` (Phase 1), +`.azure/infrastructure-plan.json` (Phase 4, status `draft`→`approved`→`deployed`), and +`infra/main.bicep` + `infra/modules/*` or `infra/main.tf` + `infra/modules/**` (Phase 6). + ## MCP Tools | Tool | Purpose | diff --git a/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/bicep-generation.md b/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/bicep-generation.md index eca57504..1db8b025 100644 --- a/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/bicep-generation.md +++ b/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/bicep-generation.md @@ -69,3 +69,25 @@ infra/ ## Validation Before Deployment Run `az bicep build --file infra/main.bicep` to validate syntax before deploying. + +## Correctness Checklist (must pass `az bicep build` with zero errors) + +Generate against these rules, then run `az bicep build` and fix in-place until clean. These are the +failures that most often break validation: + +1. **No undeclared symbols.** Every `param`, `var`, `resource`, and `module` symbol you reference is + declared in the same file. Cross-file values flow only through `module` params and `output`s. +2. **Cross-module outputs (BCP053).** When one module consumes `moduleX.outputs.Y`, that module MUST + declare `output Y ...`. Verify every consumed output exists on the producing module. +3. **`existing` references are complete.** Referenced resources use the `existing` keyword with the + correct type, `name` (and `scope`/`parent` where required); never emit a new `resource` for them. +4. **Required properties present.** Use the schema fetched in step 3 — include every required property + and use only allowed enum values and a valid, real `@apiVersion` for each type. +5. **Types match.** Parameter/variable types match their usage; no string passed where an object/int is + expected; array vs. single-object usage is consistent. +6. **`main.bicepparam` matches `main.bicep`.** Every `param` assigned in `.bicepparam` exists in + `main.bicep`; every required (non-defaulted) param is assigned; `using` points at `./main.bicep`. +7. **`targetScope` matches the deploy command** and any `resourceGroup()`/`subscription()` usage. +8. **No secrets in output.** Never `output` a secret; mark secret params `@secure()`. + +If `az bicep build` is unavailable, self-review every item above before presenting. diff --git a/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/phases/1-extract-insights.md b/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/phases/1-extract-insights.md index 6e522053..36c55d69 100644 --- a/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/phases/1-extract-insights.md +++ b/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/phases/1-extract-insights.md @@ -2,8 +2,8 @@ > The goal of this phase is to extract insights from the user's existing Azure environment. These insights will be used to guide the planning process in later phases. -1. Check whether insights already exist at `/.azure/insights.json`. If they do, reuse them and skip the rest of this phase. -2. Check whether the `insights_get` tool is available. If it is not, skip this phase. +1. Check whether insights already exist at `/.azure/insights.json`. If they do, reuse the existing entries and skip the scan in steps 2–6. In referenced mode, still execute step 7 before completing the gate; in greenfield mode, proceed to the gate. +2. If no insights file exists, check whether the `insights_get` tool is available. If it is not, initialize the file with `[]`, then continue to step 7 in referenced mode or proceed to the gate in greenfield mode. 3. Ask the user which scope to use for generating insights. Present these three options: a. "Subscription-scoped (default subscription)" — use this as the default if the user does not respond. b. "Subscription-scoped (choose a subscription)" — if selected, ask the user to provide a subscription name or ID. @@ -17,7 +17,9 @@ f. "Other" — this should be a custom input field. 5. Run the `insights_get` tool using a general-purpose subagent. Pass a one-line summary via the `--query` option that describes the user's infrastructure and the types of insights to prioritise. Do not pass the `--nocache` flag unless the user has explicitly asked for it. Begin Phase 2 while this tool runs. 6. Once the tool finishes, save the resulting JSON to `/.azure/insights.json`. Do not include tool call metadata. If the tool errors or returns no insights, write an empty array `[]` to the file instead. +7. In referenced mode, merge one insight entry for every existing resource into the current insights array. Set `existingResource.id`, `type`, `name`, `role`, `must_not_recreate: true`, and `integrationPoints` using the normalized inventory. Preserve full ARM IDs for actual-state resources and do not duplicate an entry already identified by the same resource ID. Do this even when the resource produces no broader insight. ## Gate -- `insights.json` must exist and match the Insights Schema defined in [schema.md](../schema.md). If the tool errored or returned no insights, the file should contain an empty array `[]`. \ No newline at end of file +- `insights.json` must exist and match the Insights Schema defined in [schema.md](../schema.md). If the tool errored or returned no insights, the file should contain an empty array `[]`. +- In referenced mode, every inventoried existing resource has an `existingResource` entry with `must_not_recreate: true`; actual-state entries preserve their full ARM IDs. \ No newline at end of file diff --git a/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/phases/6-generate-iac.md b/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/phases/6-generate-iac.md index 04ebc5ff..7c5e3db3 100644 --- a/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/phases/6-generate-iac.md +++ b/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/phases/6-generate-iac.md @@ -2,8 +2,27 @@ > Important: Before continuing this phase, `meta.status` must be set to `approved` as required by Phase 5. -1. Ask the user whether to generate Bicep, Terraform, or stop here. Never accept vague statements such as "continue", "yes", "go ahead", "proceed", or "make it". Only continue if the user instructs you to generate IaC and names the flavor (e.g. "yes, generate the Bicep" or "go ahead with Terraform"). +1. Ask the user whether to generate Bicep or Terraform. 2. Generate IaC from the approved plan. Refer to [bicep-generation.md](../bicep-generation.md) for Bicep or [terraform-generation.md](../terraform-generation.md) for Terraform. +3. **Apply secure-by-default (mandatory).** Unless the approved plan explicitly overrides a control, every generated resource must use: + - Private endpoints and `publicNetworkAccess: Disabled` on data/PaaS services — no unnecessary public exposure. + - Managed identity + RBAC instead of keys/connection strings; no secrets in code (`@secure()` / `sensitive = true`). + - Storage `allowSharedKeyAccess: false`; Key Vault soft-delete + purge protection; AKS managed identity with local accounts disabled. + - Minimum TLS 1.2 and encryption in transit. +4. **Validate, security-scan, and fix until clean (mandatory, self-verifying).** No-deploy, purely local: + - **Bicep:** run `az bicep build --file infra/main.bicep`. + - **Terraform:** run `terraform init -backend=false` then `terraform validate` in `infra/`. + - **Security scan:** run `checkov -d infra/` and resolve every high/critical finding. + - If any command reports errors or unresolved high/critical findings, fix the files in-place and re-run. Repeat until every command exits cleanly. + - **Prove it:** paste the exact command(s) run and their final exit status / summary into your response. Do not claim the gate passed without showing the output. If a tool is genuinely unavailable, say so explicitly and self-review against the generation correctness checklist and the secure-by-default list above. +5. **Emit the completion self-check.** End Phase 6 with this checklist, each line marked pass/fail with a one-line reason: + - [ ] Validation ran and exited clean (output shown) + - [ ] `checkov` ran; no unresolved high/critical findings + - [ ] Secure-by-default applied (private endpoints, MI/RBAC, TLS 1.2, no secrets) + - [ ] Referenced resources wired, none recreated (referenced mode only) + - [ ] Files under `infra/`; original source artifacts untouched ## Gate -- All required IaC files generated and saved to disk. +- All required IaC files generated and saved to disk under `/infra/`. +- `az bicep build` (Bicep) or `terraform validate` (Terraform) **and** `checkov` complete with zero errors and no unresolved high/critical findings, **with the command output shown in the response**. +- The completion self-check is emitted with every item passing (or an explicit, justified exception). diff --git a/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/referenced-workload.md b/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/referenced-workload.md new file mode 100644 index 00000000..6fb30ea7 --- /dev/null +++ b/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/referenced-workload.md @@ -0,0 +1,99 @@ +# Referenced-Workload Handling (on-demand) + +> Read this when the user provides something that already EXISTS — a live Azure resource / resource +> group / subscription, IaC (Bicep/Terraform/ARM) or an infra plan, or a general doc describing current +> infrastructure/requirements. If the input is purely NEW requirements with nothing existing, it's +> greenfield — ignore this file and run the normal phases. + +## The switched-up flow (referenced mode) +1. **Confirm it's referenced.** Does anything already exist (a resource group/subscription, IaC/plan, or + a doc describing current resources)? If yes, use this flow. +2. **Inventory the existing resources** from whatever was provided. Capture a COMPLETE inventory — every + resource type, plus topology/relationships (VNet/subnet, private endpoints, identity bindings) and key + configurations (SKU/tier, TLS version, public-access setting, region). Do not omit resources or invent + ones that are not present: + - **Resource group / subscription** → introspect (`az resource list` / `az graph query`) → real + resources **with resource IDs**. + - **IaC / infra plan** (Bicep/Terraform/ARM, `infrastructure-plan.json`, `terraform show -json`) → + parse `resource` / `existing` / `data` blocks → logical resources. + - **General doc** → extract any existing resources it mentions. +3. **Gather insights from ALL provided context.** Mine every input the user gave — docs, IaC comments, + resource tags, requirements, naming conventions, region, resiliency tier, PADU/preferences, cost and + compliance constraints — and record them as insights (the same channel Phase 3 already applies). + These shape the new workload just like tenant insights do. +4. **Surface them and ask which to incorporate.** Present the existing resources you found and ask which + ones to **incorporate** (reference + wire) into the new workload. Recommend a sensible default — + incorporate the ones the new workload clearly depends on; never recreate them. +5. **Generate the complete IaC** incorporating the chosen (or recommended-default) existing resources: + reference them (`existing` / `data`, real ID for live, a `param` otherwise) and **wire** the new + resources to them, honoring the gathered insights. Never emit a new `resource` for something that + already exists. + +## Answer-first (ask AND generate — never stall) +Open with a **plain-language summary of the existing infrastructure** you inventoried and the additive +change you understood, then an **explicit confirmation checkpoint** ("Confirm this understanding before I +proceed to deploy"), and **then** generate the complete, deployable IaC in the SAME response using the +recommended default. Order within the turn: (1) summary of what exists + what you'll add, (2) explicit +"confirm before I proceed" gate, (3) the generated IaC, (4) the "which to incorporate?" choice and any +assumptions. Never end a turn with only a summary or a question, and never deploy before the user +confirms. If you lack a value (region, an existing resource's ID/name), **declare a parameter and +proceed.** The confirmation gate governs deployment (Phase 7), not whether you generate the plan/IaC — +you always generate; you never deploy without an explicit, risk-acknowledged go-ahead. + +## Inputs (three shapes → one normalized inventory) +| Shape | Inputs | Referencing precision | +|---|---|---| +| **Actual state** | a live Azure resource, resource group, or subscription | real resource **IDs** → reference exactly (`existing`/`data`) | +| **Declared state** | Bicep / Terraform / ARM, or an infra plan (`infrastructure-plan.json`, `terraform show -json`) | logical → reference by **parameter** (or build, if it's a spec) | +| **Requirements/context** | any general doc with info/requirements the user wants | mine for requirements + any existing-resource mentions | + +Inputs can combine (e.g. a resource group + a requirements doc). If a declared file is ambiguous +("reference existing vs. build new?"), make the most likely assumption, state it, and generate — ask at +most one question, after the code. + +## For each existing resource, assign a ROLE (the "if needed" filter) +- **Reference + integrate** — the new workload depends on it → reference it AND **wire it in**: + RBAC role assignment, diagnostics → existing Log Analytics, private endpoints into the existing + VNet/subnet, secrets from the existing Key Vault, connection to the existing Event Hubs, use the + existing managed identity, etc. +- **Retain** — keep, must not recreate, nothing new connects → leave it; note "retained, out of scope". +- **Ignore** — irrelevant → omit. + +**Never emit a new `resource` for a resource that already exists.** Reference it. + +## Deploy (referenced mode) +Referenced mode **does not skip Phase 7** — the deliverable is deployed new infrastructure that +integrates with what already exists. Run [phases/7-deploy.md](phases/7-deploy.md) with these guardrails: + +- **Same destructive-action gate.** Present the risks and require an explicit, risk-acknowledged + "deploy" reply sent *after* the risks are shown. The original prompt never satisfies the gate. +- **Additive only.** The deployment must *add* the new resources and their wiring. The referenced + resources are declared as `existing`/`data` (not `resource`), so a normal deploy never touches them. +- **Incremental mode, never Complete.** For Bicep use the default **incremental** mode — never + `--mode Complete` (it would delete resources absent from the template, including the referenced ones). + For Terraform, the referenced resources are `data` sources / `import`ed, so `apply` must show **no + destroy** against them — abort if the plan proposes destroying or replacing a referenced resource. +- **Preview first.** Always run `az deployment ... --what-if` (Bicep) or `terraform plan` (Terraform) + and confirm the diff only *creates* new resources and *modifies nothing* on the referenced ones before + applying. +- **Deploy into the existing scope when integrating.** Target the referenced resource group/subscription + so RBAC, private endpoints, and diagnostics wire into the existing resources. + + +## How this rides the existing phases +- **Phase 1:** normalize the reference into `insights.json` — existing resources (real ID or param), + `must_not_recreate`, integration points, requirements, tier. +- **Phase 3:** existing "apply insights" logic assigns roles and picks references over new resources. +- **Phase 5:** verify — (a) no duplicate of an existing resource; (b) **every declared dependency on an + existing resource is actually wired**; (c) cross-module `outputs.X` you consume is declared by that + module (prevents `BCP053`); (d) source unmodified. +- **Phase 6:** emit new resources (secure-by-default) + `existing`/`data` references + the wiring. + Real ID inline for actual-state; a `param` for declared/doc inputs. +- **Phase 7:** DO NOT skip deploy — run Phase 7 like greenfield, honoring the destructive-action gate. + The difference is scope: deploy **only the new resources and their wiring**; never modify, recreate, + or destroy the referenced/existing resources. See "Deploy (referenced mode)" below. + +## Secure-by-default (Phase 6) +Private endpoints + `publicNetworkAccess: Disabled` on data services; managed identity over keys; +storage `allowSharedKeyAccess: false`; Key Vault soft-delete + purge protection; AKS managed identity + +disable local accounts; minimum TLS 1.2. diff --git a/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/schema.md b/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/schema.md index 2c47c6b2..cdbe2014 100644 --- a/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/schema.md +++ b/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/schema.md @@ -44,9 +44,35 @@ ## Insights Schema ```ts -{ - id: string // Stable identifier (e.g., "insight-001"); cited from inputs.insightsApplied - pattern: string // Observed fact from the tenant scan (what is true today) - implication: string // Recommended planning action derived from the pattern -}[] +type ExistingResource = { + id: string // Full ARM ID for actual state; logical or parameter reference otherwise + type: string // ARM resource type (e.g., "Microsoft.Network/virtualNetworks") + name: string + role: "reference-and-integrate" | "retain" | "ignore" + must_not_recreate: true + integrationPoints: string[] // Connections or dependencies involving the new workload +} + +type Insight = + | { + id: string // Stable identifier (e.g., "insight-001"); cited from inputs.insightsApplied + pattern: string // Observed fact from the tenant scan or referenced workload + implication: string // Recommended planning action derived from the pattern + existingResource?: ExistingResource + } + | { + id: string // Stable identifier for a resource-only entry + existingResource: ExistingResource + pattern?: string // Include with implication when the resource produces a broader insight + implication?: string + } + +type Insights = Insight[] ``` + +In referenced mode: + +- Include exactly one entry for every inventoried existing resource, uniquely identified by `existingResource.id`. +- Preserve the full ARM ID in `existingResource.id` for actual-state resources. +- Use an empty `integrationPoints` array when the resource has no integration points. +- A resource-only entry may omit `pattern` and `implication`; when recording a broader insight, include both. diff --git a/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/terraform-generation.md b/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/terraform-generation.md index 7b880604..7f38ef69 100644 --- a/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/terraform-generation.md +++ b/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/terraform-generation.md @@ -85,3 +85,27 @@ Deploy with: `terraform apply -var-file=prod.tfvars` ## Validation Before Deployment Run `terraform validate` and `terraform plan` to verify before applying. + +## Correctness Checklist (must pass `terraform validate` with zero errors) + +Generate against these rules, then run `terraform init -backend=false` + `terraform validate` and fix +in-place until clean. These are the failures that most often break validation: + +1. **Every referenced value is declared.** Each `var.X` has a `variable "X"` block; each `local.X` is + defined; each `module.X`/`azurerm_*.X` reference exists. No references to undeclared symbols. +2. **Module wiring is complete.** Values passed into a child module map to declared `variable` blocks in + that module; values read as `module.X.Y` map to declared `output "Y"` in that child module. +3. **Existing resources use `data`/`import`, not new `resource`.** Reference pre-existing infra via + `data` sources (or `import`), and wire new resources to them — never recreate them. +4. **Valid provider + required attributes.** `required_providers` pins `azurerm` (`~> 4.0`), the + `provider "azurerm"` block has `features {}`, and every resource sets its required arguments with + valid enum values and correctly-typed attributes. +5. **Correct block vs. attribute syntax.** Nested blocks (e.g. `identity`, `site_config`, + `ip_configuration`) use block syntax; scalars use `=`. No unsupported/renamed arguments for the + pinned provider version. +6. **`tfvars` match variables.** Every value in `terraform.tfvars`/`*.tfvars` corresponds to a declared + `variable`; every variable without a default is supplied. +7. **No secrets in code.** Secrets come from variables (`sensitive = true`) or Key Vault data sources, + never hardcoded literals. + +If `terraform` is unavailable, self-review every item above before presenting. diff --git a/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/workflow.md b/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/workflow.md index 816df03e..71943604 100644 --- a/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/workflow.md +++ b/.github/plugins/azure-skills/skills/azure-enterprise-infra-planner/references/workflow.md @@ -5,6 +5,8 @@ - You must execute the seven phases in sequential order. Follow the instructions precisely as defined. Do not continue to the next phase until the current phase is complete. - You must stop on all "gate" conditions and only continue when the conditions have been met. - Destructive actions require explicit user confirmation. +- **Confirmation gate vs. answer-first.** Always present a plain-language summary of your understanding plus an explicit "confirm before I proceed" checkpoint *before deploying* (Phase 7). In referenced mode you still generate the plan and IaC in the same turn (answer-first) — the gate governs *deployment*, not whether you produce the artifacts. Never end a turn with only a question, and never deploy without an explicit, risk-acknowledged go-ahead. +- **Never claim a gate passed without proof.** When a phase gate depends on a command (validation, security scan), run it and show its actual output/exit status; do not assert success from memory. - You must read each phase's reference file in full before executing it. - Never assume knowledge and cut corners or skip research steps. @@ -12,6 +14,17 @@ Starting from Phase 1, execute all phases in sequential order. Do not advance to the next phase until the current phase is complete and all of its gate conditions have been met. +## Phase 6 — hardened generation gate (apply inline) + +The detailed generation reference files may not be loaded in every environment, so the Phase 6 gate is restated here and is mandatory. After generating the IaC, and **before** offering it or advancing to deploy: + +1. **Secure-by-default.** Every resource: private endpoints + public network access disabled on data/PaaS services; managed identity + RBAC (never keys/connection strings); no secrets in code; storage shared-key access disabled; Key Vault soft-delete + purge protection; AKS managed identity with local accounts disabled; TLS 1.2 minimum. +2. **Validate + security-scan, fix until clean.** Bicep: `az bicep build --file infra/main.bicep`. Terraform: `terraform init -backend=false` then `terraform validate`. Then `checkov -d infra/`. Fix in-place and re-run until every command passes. **Paste the actual command output / exit status into your response** — never claim the gate passed without showing it. If a tool is genuinely unavailable, say so and self-review against the secure-by-default list. +3. **Completion self-check.** End Phase 6 with a checklist, each line marked pass/fail: validation clean (output shown); `checkov` no unresolved high/critical; secure-by-default applied; referenced resources wired and none recreated (referenced mode); files under `infra/` with original sources untouched. + + +> **Referenced workload?** If the user supplies something existing to reference or integrate with — a live Azure resource/resource group/subscription, a Bicep/Terraform/ARM file or infra plan, or a general doc of requirements/context — also read [referenced-workload.md](referenced-workload.md) and apply it alongside these phases. If not, run greenfield exactly as below. + | Phase | Action | Reference | Key Gate | |-------|--------|-----------|----------| | 1 | Extract insights | [1-extract-insights.md](phases/1-extract-insights.md) | Insights written to `/.azure/insights.json` | @@ -44,3 +57,6 @@ Before writing any `.bicep` or `.tf` files in Phase 6: 1. Create the `infra/` directory at `/infra/`. 2. Create `infra/modules/` for child modules. 3. Write `main.bicep` (or `main.tf`) inside `infra/`, not in the project root or `.azure/`. + + +