diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 3ed7ac345..dcbbd3823 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -26,3 +26,11 @@ Closes # - [ ] I have added an entry to `CHANGELOG.md` under `[Unreleased]` - [ ] My code follows the project's style guidelines in `CONTRIBUTING.md` - [ ] My commit messages follow [Conventional Commits](https://www.conventionalcommits.org/) + +## Standards Compliance + +- [ ] Follows [repo structure standard](https://azurelocal.cloud/standards/repo-structure) (required files present) +- [ ] Follows [naming conventions](https://azurelocal.cloud/standards/documentation/naming-conventions) (files, variables, resources) +- [ ] Uses [IIC fictional company](https://azurelocal.cloud/standards/fictional-company-policy) in all examples (never Contoso) +- [ ] Config changes validated against JSON Schema (if applicable) +- [ ] No hardcoded IPs, names, secrets, or environment-specific values in committed code diff --git a/.github/workflows/validate-repo-structure.yml b/.github/workflows/validate-repo-structure.yml new file mode 100644 index 000000000..e50c5a705 --- /dev/null +++ b/.github/workflows/validate-repo-structure.yml @@ -0,0 +1,80 @@ +name: Validate Repo Structure +on: + pull_request: + branches: [main] + +jobs: + check-structure: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Check required root files + run: | + missing=0 + for f in README.md CONTRIBUTING.md LICENSE CHANGELOG.md .gitignore; do + if [ ! -f "$f" ]; then + echo "::error::Missing required file: $f" + missing=$((missing + 1)) + fi + done + if [ $missing -gt 0 ]; then + echo "::error::$missing required root file(s) missing" + exit 1 + fi + echo "All required root files present" + + - name: Check required directories + run: | + missing=0 + for d in docs .github; do + if [ ! -d "$d" ]; then + echo "::error::Missing required directory: $d/" + missing=$((missing + 1)) + fi + done + if [ $missing -gt 0 ]; then + echo "::error::$missing required directory(s) missing" + exit 1 + fi + echo "All required directories present" + + - name: Check PR template + run: | + if [ ! -f ".github/PULL_REQUEST_TEMPLATE.md" ]; then + echo "::error::Missing .github/PULL_REQUEST_TEMPLATE.md" + exit 1 + fi + echo "PR template found" + + - name: Check config structure (if config dir exists) + run: | + if [ -d "config" ]; then + missing=0 + if [ ! -f "config/variables.example.yml" ]; then + echo "::error::Missing config/variables.example.yml" + missing=$((missing + 1)) + fi + if [ ! -f "config/schema/variables.schema.json" ]; then + echo "::error::Missing config/schema/variables.schema.json" + missing=$((missing + 1)) + fi + if [ $missing -gt 0 ]; then + exit 1 + fi + echo "Config structure valid" + else + echo "No config/ directory — skipping config checks" + fi + + - name: Check variable reference doc (if config dir exists) + run: | + if [ -d "config" ]; then + if [ ! -f "docs/reference/variables.md" ]; then + echo "::error::Missing docs/reference/variables.md (required when config/ exists)" + exit 1 + fi + echo "Variable reference doc found" + else + echo "No config/ directory — skipping variable reference check" + fi diff --git a/.gitignore b/.gitignore index 60b4e1f2a..10a97ef2b 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ yarn-error.log* # References /product-azure-local-anywhere /prodtech-docs-azl-toolkit +/project_management diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 109cb87a2..3680d7390 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -75,6 +75,15 @@ This starts a local development server at `http://localhost:3000/`. - Place in `blog/YYYY-MM-DD-slug/` directory - Include `authors` and `tags` frontmatter referencing `blog/authors.yml` and `blog/tags.yml` +## Standards + +This project follows the **org-wide AzureLocal standards** documented at [azurelocal.cloud/standards](https://azurelocal.cloud/standards/). Key references: + +- [Repository Structure](https://azurelocal.cloud/standards/repo-structure) — Required files, directories, labels, branch naming +- [Documentation Standards](https://azurelocal.cloud/standards/documentation/documentation-standards) — Writing and formatting +- [Naming Conventions](https://azurelocal.cloud/standards/documentation/naming-conventions) — Files, variables, resources +- [Fictional Company Policy](https://azurelocal.cloud/standards/fictional-company-policy) — Use IIC, never Contoso + ## Code of Conduct Be respectful and constructive. Keep discussions on-topic and collaborative. diff --git a/docs/implementation/03-key-inputs-and-variables.mdx b/docs/implementation/03-key-inputs-and-variables.mdx index bdbe15fcd..76068e124 100644 --- a/docs/implementation/03-key-inputs-and-variables.mdx +++ b/docs/implementation/03-key-inputs-and-variables.mdx @@ -735,7 +735,11 @@ After gathering all required inputs and creating your infrastructure.yml: --- -*For questions about configuration variables or validation, refer to the Configuration Management Standard or contact the Azure Local Cloud documentation team.* +*For questions about configuration variables or validation, refer to the [Variable Management Standard](./04-variable-management-standard.mdx) or contact the Azure Local Cloud documentation team.* + +:::tip Cross-Repository Standard +The **[Variable Management Standard](./04-variable-management-standard.mdx)** documents the org-wide pattern for `config/variables.example.yml`, JSON Schema validation, and `docs/reference/variables.md` across all five solution repositories. +::: --- @@ -743,7 +747,7 @@ After gathering all required inputs and creating your infrastructure.yml: | Previous | Up | Next | |----------|-----|------| -| [How to Use This Runbook](./02-how-to-use-this-runbook.mdx) | [Implementation Guide](./index.mdx) | [Prerequisites and Assumptions](./04-prerequisites-and-assumptions.mdx) | +| [How to Use This Runbook](./02-how-to-use-this-runbook.mdx) | [Implementation Guide](./index.mdx) | [Variable Management Standard](./04-variable-management-standard.mdx) | --- diff --git a/docs/implementation/04-prerequisites-and-assumptions.mdx b/docs/implementation/04-prerequisites-and-assumptions.mdx index 4fb1353f2..def250a2e 100644 --- a/docs/implementation/04-prerequisites-and-assumptions.mdx +++ b/docs/implementation/04-prerequisites-and-assumptions.mdx @@ -1,7 +1,7 @@ --- title: "Prerequisites and Assumptions" sidebar_label: "Prerequisites and Assumptions" -sidebar_position: 4 +sidebar_position: 5 description: "Requirements and assumptions for Azure Local deployment." category: "RUNBOOK" scope: "Pre-deployment requirements" diff --git a/docs/implementation/04-variable-management-standard.mdx b/docs/implementation/04-variable-management-standard.mdx new file mode 100644 index 000000000..bd94833e7 --- /dev/null +++ b/docs/implementation/04-variable-management-standard.mdx @@ -0,0 +1,201 @@ +--- +title: "Variable Management Standard" +sidebar_label: "Variable Management Standard" +sidebar_position: 4 +description: "Organization-wide variable naming, structure, and management standard for all AzureLocal solution repositories." +category: "STANDARD" +scope: "All AzureLocal solution repositories" +purpose: "Define the canonical variable management pattern used across the org" +author: "Azure Local Cloudnology Team" +created: 2026-03-17 +updated: 2026-03-17 +version: "1.0.0" +tags: + - azure-local + - configuration + - variables + - standards +keywords: + - variable management + - naming conventions + - variables.yml + - variables.example.yml + - schema validation +status: "approved" +--- + +# Variable Management Standard + +[![Standard](https://img.shields.io/badge/Type-Standard-green?style=flat-square)](./index.mdx) +[![Azure](https://img.shields.io/badge/Platform-Azure_Local-0078D4?style=flat-square&logo=microsoftazure)](https://learn.microsoft.com/en-us/azure/azure-local/) + +> **DOCUMENT CATEGORY**: Standard +> **SCOPE**: All AzureLocal solution repositories +> **PURPOSE**: Canonical variable naming, structure, and management pattern + +**Status**: Active + +--- + +## Overview + +Every AzureLocal solution repository follows a **single, standardized pattern** for managing deployment variables. This ensures consistency, validation, and automation compatibility across all solutions. + +**Core principle:** One config file, one schema, one reference doc — per repo. + +| Artifact | Location | Purpose | +|----------|----------|---------| +| Example config | `config/variables.example.yml` | Template with placeholder values — copy to `variables.yml` | +| Active config | `config/variables.yml` | Environment-specific values (**never committed**) | +| JSON Schema | `config/schema/variables.schema.json` | Validation rules enforced by CI | +| Reference doc | `docs/reference/variables.md` | Human-readable variable catalog | + +--- + +## Repository Matrix + +| Repository | Config | Schema | Reference Doc | CI Workflow | +|-----------|--------|--------|---------------|------------| +| [azurelocal-toolkit](https://github.com/AzureLocal/azurelocal-toolkit) | [variables.example.yml](https://github.com/AzureLocal/azurelocal-toolkit/blob/main/config/variables.example.yml) | [schema](https://github.com/AzureLocal/azurelocal-toolkit/blob/main/config/schema/variables.schema.json) | [reference](https://github.com/AzureLocal/azurelocal-toolkit/blob/main/docs/reference/variables.md) | `validate-config.yml` | +| [azurelocal-sofs-fslogix](https://github.com/AzureLocal/azurelocal-sofs-fslogix) | [variables.example.yml](https://github.com/AzureLocal/azurelocal-sofs-fslogix/blob/main/config/variables.example.yml) | [schema](https://github.com/AzureLocal/azurelocal-sofs-fslogix/blob/main/config/schema/variables.schema.json) | [reference](https://github.com/AzureLocal/azurelocal-sofs-fslogix/blob/main/docs/reference/variables.md) | `validate-config.yml` | +| [aurelocal-avd](https://github.com/AzureLocal/aurelocal-avd) | [variables.example.yml](https://github.com/AzureLocal/aurelocal-avd/blob/main/config/variables.example.yml) | [schema](https://github.com/AzureLocal/aurelocal-avd/blob/main/config/schema/variables.schema.json) | [reference](https://github.com/AzureLocal/aurelocal-avd/blob/main/docs/reference/variables.md) | `validate-config.yml` | +| [azurelocal-loadtools](https://github.com/AzureLocal/azurelocal-loadtools) | [variables.example.yml](https://github.com/AzureLocal/azurelocal-loadtools/blob/main/config/variables.example.yml) | [schema](https://github.com/AzureLocal/azurelocal-loadtools/blob/main/config/schema/variables.schema.json) | [reference](https://github.com/AzureLocal/azurelocal-loadtools/blob/main/docs/reference/variables.md) | `validate-config.yml` | +| [azurelocal-vm-conversion-toolkit](https://github.com/AzureLocal/azurelocal-vm-conversion-toolkit) | [variables.example.yml](https://github.com/AzureLocal/azurelocal-vm-conversion-toolkit/blob/main/config/variables.example.yml) | [schema](https://github.com/AzureLocal/azurelocal-vm-conversion-toolkit/blob/main/config/schema/variables.schema.json) | [reference](https://github.com/AzureLocal/azurelocal-vm-conversion-toolkit/blob/main/docs/reference/variables.md) | `validate-config.yml` | + +--- + +## Directory Structure + +Every repository follows this layout: + +``` +repo-root/ +├── config/ +│ ├── variables.example.yml # Committed — template +│ ├── variables.yml # .gitignored — your values +│ └── schema/ +│ └── variables.schema.json # JSON Schema for CI validation +├── docs/ +│ └── reference/ +│ └── variables.md # Human-readable variable catalog +└── .github/ + └── workflows/ + └── validate-config.yml # CI: validates example against schema +``` + +--- + +## Naming Conventions + +### File Naming + +| File | Name | Notes | +|------|------|-------| +| Example config | `variables.example.yml` | Always `.example.yml`, never `.template.yml` | +| Active config | `variables.yml` | Never committed | +| Schema | `variables.schema.json` | Draft 2020-12 JSON Schema | +| Reference doc | `variables.md` | Always at `docs/reference/variables.md` | + +### Variable Naming + +| Scope | Convention | Example | +|-------|-----------|---------| +| Top-level sections | `snake_case` | `azure_local`, `session_hosts` | +| Keys within sections | `snake_case` | `subscription_id`, `cluster_name` | +| Booleans | Descriptive name | `enable_real_time: true` | +| Secrets | `keyvault://` URI | `keyvault://kv-name/secret-name` | +| Azure resource IDs | Full ARM path | `/subscriptions/.../providers/...` | +| Enum values | `snake_case` | `auth_method: "managed_identity"` | + +### Section Organization + +Each `variables.example.yml` is organized into **logical sections** separated by comment headers: + +```yaml +# ============================================================================= +# Section Name +# ============================================================================= +section_name: + key: "value" +``` + +Common sections across repos include: +- **azure** — Subscription, tenant, resource group, location +- **keyvault** — Key Vault name and auth method +- **azure_local** — Cluster-specific configuration +- **tags** — Resource tagging (present in every repo) + +Solution-specific sections are documented in each repo's `docs/reference/variables.md`. + +--- + +## Secret Management + +Secrets are **never** stored in plaintext in `variables.yml`. All sensitive values use the `keyvault://` URI format: + +```yaml +credentials: + admin_password: "keyvault://kv-mysite/admin-password" +``` + +**Resolution flow:** + +1. Tool parses the URI → vault name + secret name +2. Tool calls `az keyvault secret show` (or SDK equivalent) +3. Secret value is passed directly — never written to disk + +Repos that use Key Vault references: **toolkit**, **sofs-fslogix**, **avd**, **loadtools** + +--- + +## Schema Validation + +Every repo includes a JSON Schema that validates the structure of `variables.example.yml`. The CI workflow runs on every PR and push to `config/**`: + +```yaml +# .github/workflows/validate-config.yml +name: Validate Config +on: + push: + paths: ['config/**'] + pull_request: + paths: ['config/**'] + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - run: pip install pyyaml jsonschema + - run: | + python -c " + import yaml, jsonschema, sys + schema = yaml.safe_load(open('config/schema/variables.schema.json')) + data = yaml.safe_load(open('config/variables.example.yml')) + jsonschema.validate(data, schema) + print('Validation PASSED') + " +``` + +--- + +## Getting Started with a New Repo + +When adding a new solution repository to the org: + +1. **Create** `config/variables.example.yml` with solution-specific sections +2. **Create** `config/schema/variables.schema.json` with Draft 2020-12 JSON Schema +3. **Create** `docs/reference/variables.md` documenting every variable with type, required, description, and default +4. **Add** `.github/workflows/validate-config.yml` (copy from any existing repo) +5. **Add** `config/variables.yml` to `.gitignore` +6. **Update this page** with a link in the Repository Matrix table + +--- + +## Related Documentation + +- **[Key Inputs and Variables](./03-key-inputs-and-variables.mdx)** — How the toolkit uses `infrastructure.yml` and `master-registry.yaml` +- **[Naming Standards](../planning/01-naming-standards.mdx)** — Azure resource naming conventions diff --git a/docs/implementation/05-authentication.mdx b/docs/implementation/05-authentication.mdx index 5e4638cd6..665ddcde1 100644 --- a/docs/implementation/05-authentication.mdx +++ b/docs/implementation/05-authentication.mdx @@ -1,7 +1,7 @@ --- title: "Authentication Methods" sidebar_label: "Authentication" -sidebar_position: 5 +sidebar_position: 6 description: "Authentication options for Azure Local deployment — Azure PowerShell and Azure CLI session setup." category: "RUNBOOK" scope: "Azure authentication for deployment scripts" diff --git a/standards/documentation/badge-library.mdx b/standards/documentation/badge-library.mdx new file mode 100644 index 000000000..a9ce3882a --- /dev/null +++ b/standards/documentation/badge-library.mdx @@ -0,0 +1,661 @@ +--- +id: badge-library +title: Badge Library +sidebar_label: Badge Library +sidebar_position: 3 +--- +# Badge Library Reference + +[![Reference](https://img.shields.io/badge/Type-Reference-purple?style=flat-square)](https://shields.io/) +[![Documentation](https://img.shields.io/badge/Category-Standards-blue?style=flat-square)](https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/) +[![Stable](https://img.shields.io/badge/Status-Stable-green?style=flat-square)](https://shields.io/) + +> **DOCUMENT CATEGORY**: Standard +> **SCOPE**: Standard badge definitions for Azure Local documentation +> **PURPOSE**: Provide copy-paste ready badge definitions for consistent documentation branding +> **MASTER REFERENCE**: [Documentation Standards](./documentation-standards.mdx) + +--- + +## Overview + +This library provides standardized badge definitions for Azure Local infrastructure documentation. All badges use the shields.io service for consistency and maintainability. + +## Quick Reference + +| Category | Purpose | Example | +|----------|---------|---------| +| **Execution Mode** | Identifies how the document/step is performed | Manual, Direct Script, Orchestrated, Automated | +| **Tools Required** | Shows what tools are needed | PowerShell, Azure CLI, Terraform, etc. | +| **IaC Platform** | Infrastructure as Code platform used | Bicep, Terraform, Ansible | +| **Status** | Document/feature maturity level | Draft, Review, Stable, Deprecated | +| **Azure Local Build** | Specifies compatible Azure Local versions | 2411, 2408, 2405 | +| **Version** | Document version for change tracking | v1.0, v2.0, etc. | +| **Environment** | Customer deployment site identifier | Customer-A, Customer-B, Customer-C | +| **Hardware** | Hardware configuration specifics | Dell AX-760, 2-Node, 4-Node, GPU | +| **Topology** | Network topology type | Switchless, Traditional, Nested | + +--- + +## Execution Mode Badges + +### Manual (Portal/GUI) + +```markdown +![Manual](https://img.shields.io/badge/Mode-Manual-blue?style=flat-square) +``` + +**Preview:** ![Manual](https://img.shields.io/badge/Mode-Manual-blue?style=flat-square) + +**Use when:** Document describes steps performed through Azure Portal, Windows Admin Center, or other GUI interfaces. + +--- + +### Direct Script + +```markdown +![Direct Script](https://img.shields.io/badge/Mode-Direct_Script-green?style=flat-square) +``` + +**Preview:** ![Direct Script](https://img.shields.io/badge/Mode-Direct_Script-green?style=flat-square) + +**Use when:** Scripts are executed directly on the target Azure Local node (local execution). + +--- + +### Orchestrated + +```markdown +![Orchestrated](https://img.shields.io/badge/Mode-Orchestrated-yellow?style=flat-square) +``` + +**Preview:** ![Orchestrated](https://img.shields.io/badge/Mode-Orchestrated-yellow?style=flat-square) + +**Use when:** Scripts are executed from a management server targeting remote Azure Local nodes via PowerShell Remoting, SSH, or similar. + +--- + +### Automated + +```markdown +![Automated](https://img.shields.io/badge/Mode-Automated-red?style=flat-square) +``` + +**Preview:** ![Automated](https://img.shields.io/badge/Mode-Automated-red?style=flat-square) + +**Use when:** Deployment uses CI/CD pipelines (GitHub Actions, GitLab CI/CD, Azure DevOps) with Infrastructure as Code. + +--- + +## Tools Required Badges + +### PowerShell + +```markdown +![PowerShell](https://img.shields.io/badge/Tool-PowerShell-5391FE?style=flat-square&logo=powershell&logoColor=white) +``` + +**Preview:** ![PowerShell](https://img.shields.io/badge/Tool-PowerShell-5391FE?style=flat-square&logo=powershell&logoColor=white) + +--- + +### Azure CLI + +```markdown +![Azure CLI](https://img.shields.io/badge/Tool-Azure_CLI-0078D4?style=flat-square&logo=microsoftazure&logoColor=white) +``` + +**Preview:** ![Azure CLI](https://img.shields.io/badge/Tool-Azure_CLI-0078D4?style=flat-square&logo=microsoftazure&logoColor=white) + +--- + +### Azure Portal + +```markdown +![Azure Portal](https://img.shields.io/badge/Tool-Azure_Portal-0078D4?style=flat-square&logo=microsoftazure&logoColor=white) +``` + +**Preview:** ![Azure Portal](https://img.shields.io/badge/Tool-Azure_Portal-0078D4?style=flat-square&logo=microsoftazure&logoColor=white) + +--- + +### Windows Admin Center + +```markdown +![WAC](https://img.shields.io/badge/Tool-Windows_Admin_Center-0078D4?style=flat-square&logo=windows&logoColor=white) +``` + +**Preview:** ![WAC](https://img.shields.io/badge/Tool-Windows_Admin_Center-0078D4?style=flat-square&logo=windows&logoColor=white) + +--- + +### Dell OpenManage + +```markdown +![Dell OpenManage](https://img.shields.io/badge/Tool-Dell_OpenManage-007DB8?style=flat-square&logo=dell&logoColor=white) +``` + +**Preview:** ![Dell OpenManage](https://img.shields.io/badge/Tool-Dell_OpenManage-007DB8?style=flat-square&logo=dell&logoColor=white) + +--- + +### iDRAC + +```markdown +![iDRAC](https://img.shields.io/badge/Tool-iDRAC-007DB8?style=flat-square&logo=dell&logoColor=white) +``` + +**Preview:** ![iDRAC](https://img.shields.io/badge/Tool-iDRAC-007DB8?style=flat-square&logo=dell&logoColor=white) + +--- + +### SSH + +```markdown +![SSH](https://img.shields.io/badge/Tool-SSH-4EAA25?style=flat-square&logo=gnubash&logoColor=white) +``` + +**Preview:** ![SSH](https://img.shields.io/badge/Tool-SSH-4EAA25?style=flat-square&logo=gnubash&logoColor=white) + +--- + +### WinRM + +```markdown +![WinRM](https://img.shields.io/badge/Tool-WinRM-0078D6?style=flat-square&logo=windows&logoColor=white) +``` + +**Preview:** ![WinRM](https://img.shields.io/badge/Tool-WinRM-0078D6?style=flat-square&logo=windows&logoColor=white) + +--- + +## IaC Platform Badges + +### Bicep + +```markdown +![Bicep](https://img.shields.io/badge/IaC-Bicep-9B4F96?style=flat-square&logo=microsoftazure&logoColor=white) +``` + +**Preview:** ![Bicep](https://img.shields.io/badge/IaC-Bicep-9B4F96?style=flat-square&logo=microsoftazure&logoColor=white) + +--- + +### Terraform + +```markdown +![Terraform](https://img.shields.io/badge/IaC-Terraform-7B42BC?style=flat-square&logo=terraform&logoColor=white) +``` + +**Preview:** ![Terraform](https://img.shields.io/badge/IaC-Terraform-7B42BC?style=flat-square&logo=terraform&logoColor=white) + +--- + +### Ansible + +```markdown +![Ansible](https://img.shields.io/badge/IaC-Ansible-EE0000?style=flat-square&logo=ansible&logoColor=white) +``` + +**Preview:** ![Ansible](https://img.shields.io/badge/IaC-Ansible-EE0000?style=flat-square&logo=ansible&logoColor=white) + +--- + +### ARM Templates + +```markdown +![ARM](https://img.shields.io/badge/IaC-ARM_Templates-0078D4?style=flat-square&logo=microsoftazure&logoColor=white) +``` + +**Preview:** ![ARM](https://img.shields.io/badge/IaC-ARM_Templates-0078D4?style=flat-square&logo=microsoftazure&logoColor=white) + +--- + +## Status Badges + +### Draft + +```markdown +![Draft](https://img.shields.io/badge/Status-Draft-orange?style=flat-square) +``` + +**Preview:** ![Draft](https://img.shields.io/badge/Status-Draft-orange?style=flat-square) + +**Use when:** Document is work in progress, not ready for production use. + +--- + +### Incomplete + +```markdown +![Incomplete](https://img.shields.io/badge/Status-Incomplete-red?style=flat-square) +``` + +**Preview:** ![Incomplete](https://img.shields.io/badge/Status-Incomplete-red?style=flat-square) + +**Use when:** Document is actively being updated or hasn't been finished yet. + +--- + +### Review + +```markdown +![Review](https://img.shields.io/badge/Status-Review-yellow?style=flat-square) +``` + +**Preview:** ![Review](https://img.shields.io/badge/Status-Review-yellow?style=flat-square) + +**Use when:** Document is complete but pending technical/peer review. + +--- + +### Stable + +```markdown +![Stable](https://img.shields.io/badge/Status-Stable-green?style=flat-square) +``` + +**Preview:** ![Stable](https://img.shields.io/badge/Status-Stable-green?style=flat-square) + +**Use when:** Document is approved and production-ready. + +--- + +### Deprecated + +```markdown +![Deprecated](https://img.shields.io/badge/Status-Deprecated-red?style=flat-square) +``` + +**Preview:** ![Deprecated](https://img.shields.io/badge/Status-Deprecated-red?style=flat-square) + +**Use when:** Document is outdated and superseded by newer version. + +--- + +## Azure Local Build Badges + +### Build 2411 (Latest) + +```markdown +![Azure Local 2411](https://img.shields.io/badge/Azure_Local-2411-0078D4?style=flat-square) +``` + +**Preview:** ![Azure Local 2411](https://img.shields.io/badge/Azure_Local-2411-0078D4?style=flat-square) + +--- + +### Build 2408 + +```markdown +![Azure Local 2408](https://img.shields.io/badge/Azure_Local-2408-0078D4?style=flat-square) +``` + +**Preview:** ![Azure Local 2408](https://img.shields.io/badge/Azure_Local-2408-0078D4?style=flat-square) + +--- + +### Build 2405 + +```markdown +![Azure Local 2405](https://img.shields.io/badge/Azure_Local-2405-0078D4?style=flat-square) +``` + +**Preview:** ![Azure Local 2405](https://img.shields.io/badge/Azure_Local-2405-0078D4?style=flat-square) + +--- + +### Build 2402 + +```markdown +![Azure Local 2402](https://img.shields.io/badge/Azure_Local-2402-0078D4?style=flat-square) +``` + +**Preview:** ![Azure Local 2402](https://img.shields.io/badge/Azure_Local-2402-0078D4?style=flat-square) + +--- + +### Multiple Builds + +```markdown +![Azure Local 2405+](https://img.shields.io/badge/Azure_Local-2405+-0078D4?style=flat-square) +``` + +**Preview:** ![Azure Local 2405+](https://img.shields.io/badge/Azure_Local-2405+-0078D4?style=flat-square) + +--- + +## Environment Badges + +### Site A (2-Node Switchless) + +```markdown +![Site-A](https://img.shields.io/badge/Environment-Site--A-orange?style=flat-square&logo=microsoftazure&logoColor=white) +``` + +**Preview:** ![Site-A](https://img.shields.io/badge/Environment-Site--A-orange?style=flat-square&logo=microsoftazure&logoColor=white) + +**Use when:** Documentation relates to Site A deployment (2-node switchless configuration). + +--- + +### Site B (4-Node Datacenter) + +```markdown +![Site-B](https://img.shields.io/badge/Environment-Site--B-green?style=flat-square&logo=microsoftazure&logoColor=white) +``` + +**Preview:** ![Site-B](https://img.shields.io/badge/Environment-Site--B-green?style=flat-square&logo=microsoftazure&logoColor=white) + +**Use when:** Documentation relates to Site B deployment (4-node datacenter configuration). + +--- + +### Site C (8-Node Production) + +```markdown +![Site-C](https://img.shields.io/badge/Environment-Site--C-blue?style=flat-square&logo=microsoftazure&logoColor=white) +``` + +**Preview:** ![Site-C](https://img.shields.io/badge/Environment-Site--C-blue?style=flat-square&logo=microsoftazure&logoColor=white) + +**Use when:** Documentation relates to Site C deployment (8-node production cluster). + +--- + +## Hardware Configuration Badges + +### Dell AX-760 + +```markdown +![Dell AX-760](https://img.shields.io/badge/Hardware-Dell_AX--760-007DB8?style=flat-square&logo=dell&logoColor=white) +``` + +**Preview:** ![Dell AX-760](https://img.shields.io/badge/Hardware-Dell_AX--760-007DB8?style=flat-square&logo=dell&logoColor=white) + +**Use when:** Documentation is specific to Dell AX-760 hardware platform. + +--- + +### 2-Node Cluster + +```markdown +![2-Node](https://img.shields.io/badge/Cluster-2--Node-blue?style=flat-square) +``` + +**Preview:** ![2-Node](https://img.shields.io/badge/Cluster-2--Node-blue?style=flat-square) + +**Use when:** Documentation applies to 2-node Azure Local clusters. + +--- + +### 4-Node Cluster + +```markdown +![4-Node](https://img.shields.io/badge/Cluster-4--Node-green?style=flat-square) +``` + +**Preview:** ![4-Node](https://img.shields.io/badge/Cluster-4--Node-green?style=flat-square) + +**Use when:** Documentation applies to 4-node Azure Local clusters. + +--- + +### GPU-Enabled + +```markdown +![GPU](https://img.shields.io/badge/Hardware-GPU_Enabled-76B900?style=flat-square&logo=nvidia&logoColor=white) +``` + +**Preview:** ![GPU](https://img.shields.io/badge/Hardware-GPU_Enabled-76B900?style=flat-square&logo=nvidia&logoColor=white) + +**Use when:** Documentation involves GPU acceleration (NVIDIA L4, etc.). + +--- + +## Topology Badges + +### Switchless Topology + +```markdown +![Switchless](https://img.shields.io/badge/Topology-Switchless-purple?style=flat-square) +``` + +**Preview:** ![Switchless](https://img.shields.io/badge/Topology-Switchless-purple?style=flat-square) + +**Use when:** Documentation applies to switchless network topology (direct RDMA connections). + +--- + +### Traditional Switch Topology + +```markdown +![Traditional](https://img.shields.io/badge/Topology-Traditional-blue?style=flat-square) +``` + +**Preview:** ![Traditional](https://img.shields.io/badge/Topology-Traditional-blue?style=flat-square) + +**Use when:** Documentation applies to traditional switch-based network topology. + +--- + +### Nested Virtualization + +```markdown +![Nested](https://img.shields.io/badge/Topology-Nested_Virtualization-orange?style=flat-square) +``` + +**Preview:** ![Nested](https://img.shields.io/badge/Topology-Nested_Virtualization-orange?style=flat-square) + +**Use when:** Documentation applies to nested Hyper-V virtualization environments. + +--- + +## Version Badges + +```markdown +![Version](https://img.shields.io/badge/Version-1.0-blue?style=flat-square) +![Version](https://img.shields.io/badge/Version-2.0-blue?style=flat-square) +![Version](https://img.shields.io/badge/Version-1.1-blue?style=flat-square) +``` + +**Preview:** + +- ![Version 1.0](https://img.shields.io/badge/Version-1.0-blue?style=flat-square) +- ![Version 2.0](https://img.shields.io/badge/Version-2.0-blue?style=flat-square) +- ![Version 1.1](https://img.shields.io/badge/Version-1.1-blue?style=flat-square) + +--- + +## CI/CD Platform Badges + +### GitHub Actions + +```markdown +![GitHub Actions](https://img.shields.io/badge/CI-GitHub_Actions-2088FF?style=flat-square&logo=githubactions&logoColor=white) +``` + +**Preview:** ![GitHub Actions](https://img.shields.io/badge/CI-GitHub_Actions-2088FF?style=flat-square&logo=githubactions&logoColor=white) + +--- + +### GitLab CI/CD + +```markdown +![GitLab CI](https://img.shields.io/badge/CI-GitLab_CI/CD-FC6D26?style=flat-square&logo=gitlab&logoColor=white) +``` + +**Preview:** ![GitLab CI](https://img.shields.io/badge/CI-GitLab_CI/CD-FC6D26?style=flat-square&logo=gitlab&logoColor=white) + +--- + +### Azure DevOps + +```markdown +![Azure DevOps](https://img.shields.io/badge/CI-Azure_DevOps-0078D7?style=flat-square&logo=azuredevops&logoColor=white) +``` + +**Preview:** ![Azure DevOps](https://img.shields.io/badge/CI-Azure_DevOps-0078D7?style=flat-square&logo=azuredevops&logoColor=white) + +--- + +## Combined Badge Examples + +### Site A - 2-Node Switchless (Manual) + +```markdown +![Manual](https://img.shields.io/badge/Mode-Manual-blue?style=flat-square) +![Site-A](https://img.shields.io/badge/Environment-Site--A-orange?style=flat-square&logo=microsoftazure&logoColor=white) +![2-Node](https://img.shields.io/badge/Cluster-2--Node-blue?style=flat-square) +![Switchless](https://img.shields.io/badge/Topology-Switchless-purple?style=flat-square) +![Azure Local 2411](https://img.shields.io/badge/Azure_Local-2411-0078D4?style=flat-square) +![Stable](https://img.shields.io/badge/Status-Stable-green?style=flat-square) +![Version 1.0](https://img.shields.io/badge/Version-1.0-blue?style=flat-square) +``` + +--- + +### Site B - 4-Node Datacenter (Direct Script) + +```markdown +![Direct Script](https://img.shields.io/badge/Mode-Direct_Script-green?style=flat-square) +![Site-B](https://img.shields.io/badge/Environment-Site--B-green?style=flat-square&logo=microsoftazure&logoColor=white) +![Dell AX-760](https://img.shields.io/badge/Hardware-Dell_AX--760-007DB8?style=flat-square&logo=dell&logoColor=white) +![4-Node](https://img.shields.io/badge/Cluster-4--Node-green?style=flat-square) +![PowerShell](https://img.shields.io/badge/Tool-PowerShell-5391FE?style=flat-square&logo=powershell&logoColor=white) +![Azure Local 2411](https://img.shields.io/badge/Azure_Local-2411-0078D4?style=flat-square) +![Stable](https://img.shields.io/badge/Status-Stable-green?style=flat-square) +![Version 1.0](https://img.shields.io/badge/Version-1.0-blue?style=flat-square) +``` + +--- + +### Site C - 8-Node Production (Orchestrated) + +```markdown +![Orchestrated](https://img.shields.io/badge/Mode-Orchestrated-yellow?style=flat-square) +![Site-C](https://img.shields.io/badge/Environment-Site--C-blue?style=flat-square&logo=microsoftazure&logoColor=white) +![Dell AX-760](https://img.shields.io/badge/Hardware-Dell_AX--760-007DB8?style=flat-square&logo=dell&logoColor=white) +![4-Node](https://img.shields.io/badge/Cluster-4--Node-green?style=flat-square) +![Traditional](https://img.shields.io/badge/Topology-Traditional-blue?style=flat-square) +![PowerShell](https://img.shields.io/badge/Tool-PowerShell-5391FE?style=flat-square&logo=powershell&logoColor=white) +![WinRM](https://img.shields.io/badge/Tool-WinRM-0078D6?style=flat-square&logo=windows&logoColor=white) +![Azure Local 2411](https://img.shields.io/badge/Azure_Local-2411-0078D4?style=flat-square) +![Stable](https://img.shields.io/badge/Status-Stable-green?style=flat-square) +![Version 1.0](https://img.shields.io/badge/Version-1.0-blue?style=flat-square) +``` + +--- + +### Test Environment - Nested Virtual (Automated) + +```markdown +![Automated](https://img.shields.io/badge/Mode-Automated-red?style=flat-square) +![TestEnvironment](https://img.shields.io/badge/Environment-TestEnvironment-yellow?style=flat-square&logo=microsoftazure&logoColor=white) +![Nested](https://img.shields.io/badge/Topology-Nested_Virtualization-orange?style=flat-square) +![Terraform](https://img.shields.io/badge/IaC-Terraform-7B42BC?style=flat-square&logo=terraform&logoColor=white) +![GitLab CI](https://img.shields.io/badge/CI-GitLab_CI/CD-FC6D26?style=flat-square&logo=gitlab&logoColor=white) +![Azure Local 2411](https://img.shields.io/badge/Azure_Local-2411-0078D4?style=flat-square) +![Stable](https://img.shields.io/badge/Status-Stable-green?style=flat-square) +![Version 1.0](https://img.shields.io/badge/Version-1.0-blue?style=flat-square) +``` + +--- + +### Deployment Management Hub (Automated) + +```markdown +![Automated](https://img.shields.io/badge/Mode-Automated-red?style=flat-square) +![ManagementHub](https://img.shields.io/badge/Environment-ManagementHub-purple?style=flat-square&logo=microsoftazure&logoColor=white) +![Bicep](https://img.shields.io/badge/IaC-Bicep-9B4F96?style=flat-square&logo=microsoftazure&logoColor=white) +![GitHub Actions](https://img.shields.io/badge/CI-GitHub_Actions-2088FF?style=flat-square&logo=githubactions&logoColor=white) +![Azure Local 2411](https://img.shields.io/badge/Azure_Local-2411-0078D4?style=flat-square) +![Stable](https://img.shields.io/badge/Status-Stable-green?style=flat-square) +![Version 1.0](https://img.shields.io/badge/Version-1.0-blue?style=flat-square) +``` + +--- + +## Badge Styling Options + +All badges use `style=flat-square` by default. Other style options: + +| Style | URL Parameter | Appearance | +|-------|---------------|------------| +| **Flat Square** | `style=flat-square` | Recommended default | +| Flat | `style=flat` | Rounded edges | +| Plastic | `style=plastic` | Glossy 3D look | +| For The Badge | `style=for-the-badge` | Large/prominent | +| Social | `style=social` | GitHub-like style | + +**Example switching style:** + +```markdown +![Flat](https://img.shields.io/badge/Mode-Manual-blue?style=flat) +![Flat Square](https://img.shields.io/badge/Mode-Manual-blue?style=flat-square) +![Plastic](https://img.shields.io/badge/Mode-Manual-blue?style=plastic) +![For The Badge](https://img.shields.io/badge/Mode-Manual-blue?style=for-the-badge) +``` + +--- + +## Custom Badge Colors + +To create a custom badge color, use hex codes without the `#`: + +```markdown +![Custom](https://img.shields.io/badge/Label-Text-FF5733?style=flat-square) +``` + +**Common colors used:** + +- **Blue**: `0078D4` (Azure blue), `blue` +- **Green**: `green`, `4EAA25` +- **Yellow**: `yellow`, `F7B500` +- **Orange**: `orange`, `FF8C00` +- **Red**: `red`, `EE0000` +- **Purple**: `7B42BC`, `9B4F96` +- **Dell Blue**: `007DB8` +- **NVIDIA Green**: `76B900` + +--- + +## Badge URL Structure + +Understanding the shields.io URL pattern: + +``` +https://img.shields.io/badge/{label}-{message}-{color}?{parameters} +``` + +**Components:** + +- `{label}`: Left side text (e.g., "Environment") +- `{message}`: Right side text (e.g., "Site-A") +- `{color}`: Badge color (hex or name) +- `{parameters}`: Optional parameters (style, logo, etc.) + +**Common parameters:** + +- `style=flat-square` - Badge style +- `logo=microsoftazure` - Logo name from Simple Icons +- `logoColor=white` - Logo color + +--- + +## Related Documentation + +- [Documentation Standards](./documentation-standards.mdx) - Complete documentation framework +- [DrawIO Diagramming Standard](./drawio-diagramming-standard.mdx) - Diagram standards + +--- + +**Version Control** + +- Created: 2025-12-10 by Product Technology Team +- Last Edited: 2026-02-08 by Product Technology Team +- Version: 1.1.0 +- Tags: standards, badges, documentation, branding +- Keywords: badges, shields.io, documentation, azure-local, environment, hardware +- Author: Product Technology Team + diff --git a/standards/documentation/documentation-standards.mdx b/standards/documentation/documentation-standards.mdx new file mode 100644 index 000000000..0c90513f3 --- /dev/null +++ b/standards/documentation/documentation-standards.mdx @@ -0,0 +1,1012 @@ +--- +id: documentation-standards +title: Documentation Standards +sidebar_label: Documentation Standards +sidebar_position: 1 +--- + +# Documentation Standards + +[![Standards](https://img.shields.io/badge/Standards-Documentation-purple?logo=book)](https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange)](https://tierpoint.com) +[![Best-Practices](https://img.shields.io/badge/Best-Practices-green?logo=microsoftazure)](https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/) + +> **DOCUMENT CATEGORY**: Standards +> **SCOPE**: Universal documentation standards for all repositories +> **PURPOSE**: Define comprehensive documentation standards for structure, format, and quality +> **MASTER REFERENCE**: This document is the master reference for all documentation + +**Status**: Active +**Applies To**: All Azure Local environment repositories +**Last Updated**: 2025-12-10 + +--- + +## Purpose + +Establish comprehensive documentation standards to ensure: + +- **Consistency** across all environments +- **Quality** in technical writing +- **Maintainability** of documentation +- **Accessibility** for all team members +- **Compliance** with industry best practices + +--- + +## Documentation Principles + +### 1. **Documentation-First Approach** + +- Document BEFORE implementing +- Keep documentation current with code +- Treat docs as code (version control, review, test) + +### 2. **Single Source of Truth** + +- One authoritative document per topic +- Cross-reference, don't duplicate +- Clear ownership and maintenance responsibility + +### 3. **Audience-Aware** + +- Write for specific audiences (operators, developers, executives) +- Include appropriate technical depth +- Use consistent terminology + +### 4. **Actionable Content** + +- Provide step-by-step procedures +- Include examples and code samples +- Specify prerequisites and outcomes + +--- + +## Example Company & Branding Policy + +:::danger MANDATORY — Read before writing any example +This policy applies to **every** document, code sample, script example, YAML snippet, Terraform block, diagram, and configuration reference across all repositories in this workspace. +::: + +### The Rule + +There are exactly **two company names** that may appear in this documentation, and they serve **different, non-interchangeable purposes**: + +| Name | When to Use | What It Represents | +|------|-------------|--------------------| +| **TierPoint** (or **Hybrid Cloud Solutions**) | Authorship, copyright, team references, product ownership, internal tooling labels | The **real company** that builds and maintains this platform | +| **Infinite Improbability Corp** | All examples, code samples, walkthroughs, sample configs, placeholder values | A **fictional customer** — like Microsoft's "Contoso" | + +### Why This Matters + +- Documentation describes a **multi-tenant platform** deployed to many different customers +- Examples must **never** use a real customer name, real credentials, or real environment identifiers +- Examples must **never** use TierPoint's own internal environment names (e.g., `tpdemos`, `tplabs`, `tpprod`) — those are real environments, not examples +- Using a single, consistent fictional company makes examples instantly recognizable as replaceable placeholders + +### Infinite Improbability Corp — Reference Card + +| Attribute | Standard Value | +|-----------|----------------| +| **Full Name** | Infinite Improbability Corp | +| **Domain** | `improbability.cloud` | +| **Short Prefix / Site Code** | `iic` | +| **NetBIOS Name** | `IMPROBABLE` | +| **Entra Tenant Domain** | `improbability.onmicrosoft.com` | +| **AD Domain (on-prem)** | `iic.local` or `ad.improbability.cloud` | +| **Email Pattern** | `user@improbability.cloud` | +| **Cluster Naming** | `iic-clus01` | +| **Node Naming** | `iic-01-n01`, `iic-01-n02`, `iic-01-n03`, `iic-01-n04` | +| **iDRAC IPs (example)** | `10.0.0.11`, `10.0.0.12`, `10.0.0.13`, `10.0.0.14` | +| **Management IPs (example)** | `192.168.1.11`, `192.168.1.12`, etc. | +| **Key Vault (example)** | `kv-iic-platform` | +| **Resource Group (example)** | `rg-iic-azl-eus-01` | +| **Location (fictional)** | `Sector ZZ9 Plural Z Alpha` | + +### TierPoint — When It Appears + +Use the real company name **only** for: + +- Copyright notices and license headers +- Author / team attribution (e.g., `Author: Product Technology Team` or `TierPoint ProdTech`) +- Badge links back to `tierpoint.com` +- References to TierPoint as the platform provider ("TierPoint deploys Azure Local for customers") +- Internal tool names that include the company name (e.g., repo names like `prodtech-docs-*`) +- CODEOWNERS, CONTRIBUTING.md, and governance documents + +### What Is NOT Allowed + +| Violation | Why | Fix | +|-----------|-----|-----| +| Using `tpdemos`, `tplabs`, `tpprod` in examples | These are real TierPoint environments | Use `iic` prefix instead | +| Using `configs/infrastructure-tpdemos.yml` in docs | Exposes internal environment naming | Use `configs/infrastructure.yml` (generic) | +| Using real customer names anywhere | Privacy / confidentiality | Use Infinite Improbability Corp | +| Using `contoso`, `fabrikam`, `northwind` | Those are Microsoft's fictional companies, not ours | Use Infinite Improbability Corp | +| Using `example.com` or `test.com` in examples | Generic and unprofessional | Use `improbability.cloud` | +| Mixing fictional company names within one document | Confusing and inconsistent | One fictional company: Infinite Improbability Corp | + +### Code Sample Template + +When writing any code example, use these values: + +```powershell title="Example: Config-driven script invocation" +# Correct — uses generic config path and fictional company values +.\Invoke-ConfigureNTP-Orchestrated.ps1 -ConfigPath "configs/infrastructure.yml" + +# Correct — uses IIC example node names +.\Invoke-ArcRegistration-Orchestrated.ps1 -ConfigPath "configs/infrastructure.yml" -TargetNode "iic-01-n01" +``` + +```yaml title="Example: infrastructure.yml site block" +site: + code: "IIC" + name: "Infinite Improbability Corp" + location: "Sector ZZ9 Plural Z Alpha" + environment: "Production" +``` + +:::info Existing Usage +Several documents already follow this standard: +- `lld-azure-local-anywhere.mdx` — IIC reference card at top +- `03-key-inputs-and-variables.mdx` — IIC placeholder examples throughout +- `examples.mdx` — Full IIC infrastructure.yml sample +- `master-registry.yaml` — IIC environment references in header + +Documents that **still need updating** are tracked in [TODO → Standards & Compliance](../../TODO.md). +::: + +--- + +## Document Metadata + +### YAML Frontmatter (Required) + +All documents must include YAML frontmatter at the beginning of the file: + +```yaml +--- +title: "Document Title" +description: "Brief description of the document" +category: "RUNBOOK | GUIDE | REFERENCE | DESIGN | HOWTO | STANDARDS" +scope: "Brief scope statement" +purpose: "Document purpose" +author: "Author Name or Team" +created: YYYY-MM-DD +updated: YYYY-MM-DD +version: "1.0.0" +tags: + - tag1 + - tag2 +keywords: + - keyword1 + - keyword2 +status: "draft | review | approved | deprecated" +--- +``` + +**Field Descriptions:** + +- **title**: Full document title +- **description**: 1-2 sentence description +- **category**: Document type (see Document Types section) +- **scope**: What the document covers +- **purpose**: Why the document exists +- **author**: Author or team name +- **created**: Initial creation date +- **updated**: Last update date +- **version**: Semantic version (major.minor.patch) +- **tags**: Topic tags for categorization +- **keywords**: Searchable keywords +- **status**: Current document status + +### Document Header Format + +After YAML frontmatter, include a metadata block: + +```markdown +# Document Title + +> **DOCUMENT CATEGORY**: [Type] +> **SCOPE**: [Scope statement] +> **PURPOSE**: [Purpose statement] +> **MASTER REFERENCE**: [Link to authoritative source if applicable] + +[![Badge1](url)](link) +[![Badge2](url)](link) +``` + +**Badge Requirements:** + +- Minimum 2 badges, maximum 5 badges +- Use badges from [Badge Library](./badge-library.mdx) +- First badge should indicate document type or domain +- Include TierPoint badge for organizational documents + +--- + +## Required Documentation + +### Every Repository Must Have + +| Document | Location | Purpose | Audience | +|----------|----------|---------|----------| +| **README.md** | Root | Repository overview, quick start | All | +| **CHANGELOG.md** | Root | Version history | All | +| **docs/README.md** | docs/ | Documentation index | All | +| **docs/configuration/** | docs/configuration/ | Configuration docs | Operators, Developers | +| **docs/guides/** | docs/guides/ | How-to procedures | Operators | +| **docs/design/** | docs/design/ | Architecture decisions | Architects, Developers | + +--- + +## Document Structure Standards + +### README.md Format + +Every `README.md` should include: + +```markdown +# {Project/Directory Name} + +[![Badge1](url)](link) [![Badge2](url)](link) + +**{One-sentence description}** + +## Overview + +{Brief overview of purpose and scope} + +## Structure + +{Describe what's in this directory/repo} + +## Quick Start + +{How to use this} + +## Additional Resources + +{Links to related docs} + +--- + +**Version Control** +- Created: YYYY-MM-DD by {Author} +- Last Updated: YYYY-MM-DD +- Version: X.Y.Z +- Tags: {comma-separated tags} +``` + +**Example**: See [Golden Template](./templates/golden-template.mdx) + +--- + +### Standard Document Header + +All documentation files should include: + +```markdown +# {Document Title} + +**Status**: Active | Draft | Archived +**Applies To**: {Specific repos or "All environments"} +**Last Updated**: YYYY-MM-DD + +--- + +## Purpose + +{Clear statement of document purpose} + +--- +``` + +--- + +### Standard Document Footer + +All documentation files should end with: + +```markdown +--- + +**Version Control** +- Created: YYYY-MM-DD by {Author} +- Last Updated: YYYY-MM-DD by {Author} +- Version: X.Y.Z +- Tags: {comma-separated tags} +- Keywords: {comma-separated keywords} +- Author: {Team/Individual} +``` + +--- + +## Formatting Standards + +### Headings + +```markdown +# H1 - Document Title (only one per document) + +## H2 - Major Sections + +### H3 - Sub-sections + +#### H4 - Detailed Items + +##### H5 - Rarely Used + +###### H6 - Almost Never Used +``` + +**Guidelines**: + +- Use H1 only for document title +- Use H2 for major sections +- Don't skip heading levels +- Keep headings descriptive but concise +- **Do NOT use emojis in headings** - emojis are unprofessional and inconsistent across platforms + +### Emoji Policy + +:::warning No Emojis in Documentation +**Emojis are NOT permitted** in professional documentation. This includes: +- Section headings (e.g., `## 📋 Overview` - incorrect) +- Metadata blocks (e.g., `> **📋 CATEGORY**` - incorrect) +- Status indicators (e.g., `✅ Active` - use text instead) +- Inline content decorations + +**Why?** +- Emojis render inconsistently across platforms and browsers +- They are unprofessional for enterprise documentation +- They can cause accessibility issues with screen readers +- They add visual noise without adding information value + +**Instead of emojis, use:** +- Clear, descriptive text labels +- Badges for status indicators (via shields.io) +- Admonitions/callouts for emphasis (:::tip, :::warning, etc.) +::: + +--- + +### Lists + +**Unordered Lists**: + +```markdown +- Item one +- Item two + - Sub-item + - Sub-item +- Item three +``` + +**Ordered Lists**: + +```markdown +1. First step +2. Second step +3. Third step + 1. Sub-step + 2. Sub-step +``` + +**Task Lists**: + +```markdown +- [x] Completed task +- [ ] Pending task +- [ ] Future task +``` + +--- + +### Code Blocks + +**Inline Code**: + +```markdown +Use `backticks` for inline code, commands, or filenames. +``` + +**Code Blocks with Language**: + +````markdown +```powershell +# PowerShell example +$clusterName = "iic-clus01" +Deploy-Cluster -Name $clusterName +``` + +```bash +# Bash example +CLUSTER_NAME="iic-clus01" +deploy-cluster --name "$CLUSTER_NAME" +``` + +```yaml +# YAML example +cluster: + name: iic-clus01 + nodes: 4 +``` +```` + +**Always specify language** for syntax highlighting! + +--- + +### Tables + +**Standard Table**: + +```markdown +| Column 1 | Column 2 | Column 3 | +|----------|----------|----------| +| Value 1 | Value 2 | Value 3 | +| Value 4 | Value 5 | Value 6 | +``` + +**Aligned Table**: + +```markdown +| Left Aligned | Center Aligned | Right Aligned | +|:-------------|:--------------:|--------------:| +| Left | Center | Right | +``` + +**Guidelines**: + +- Use tables for structured data +- Keep tables readable in source +- Use alignment appropriately +- Consider breaking very wide tables + +--- + +### Links + +**Internal Links** (within repo): + +```markdown +[Link Text](../relative/path/to/file.md) +[Configuration Guide](./docs/guides/configuration-guide.md) +``` + +**External Links**: + +```markdown +[Microsoft Docs](https://docs.microsoft.com) +``` + +**Reference Links** (for readability): + +```markdown +This is [a link][1] to documentation. + +[1]: https://docs.microsoft.com +``` + +--- + +### Images + +```markdown +![Alt Text](./images/diagram.png) +![Network Diagram](../diagrams/network-topology.png) +``` + +**Guidelines**: + +- Store images in `docs/images/` or `docs/diagrams/` +- Use descriptive alt text +- Keep image files reasonably sized (\<1MB) +- Use PNG for diagrams, JPG for photos + +--- + +### Badges + +Use shields.io badges for status indicators: + +```markdown +[![Status](https://img.shields.io/badge/Status-Active-green)]() +[![Environment](https://img.shields.io/badge/Environment-Production-red)]() +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange)]() +``` + +See [Badge Library](./badge-library.mdx) for complete badge reference and usage examples. + +--- + +## Visual Conventions + +### Callout Boxes (Admonitions) + +Use Docusaurus admonitions for important information. **Do NOT use emoji-prefixed blockquotes.** + +```markdown +:::note +General information or tips. +::: + +:::warning +Important caution or potential issue. +::: + +:::tip +Helpful suggestion or best practice. +::: + +:::info +Reference or informational content. +::: + +:::danger +Critical error or problem to avoid. +::: +``` + +**Example Usage:** + +:::tip +Use `-WhatIf` parameter to preview changes before executing. +::: + +:::warning +This operation will delete all resources in the resource group. +::: + +:::info +For more details, see [Azure Naming Standards](naming-conventions.mdx). +::: + +--- + +## Navigation Model + +### Section Navigation + +At the end of each major section, include navigation links: + +```markdown +--- + +**Navigation:** +- [Back to Section TOC](#section-table-of-contents) +- [Back to Main TOC](#table-of-contents) +- [Next: Section Name](#next-section) +``` + +### Quick Navigation Links + +For longer documents, include quick navigation after the document header: + +```markdown +## Quick Navigation +[Section 1](#section-1) | [Section 2](#section-2) | [Section 3](#section-3) | [Appendices](#appendices) + +**Quick Links:** [related-doc1](link) • [related-doc2](link) • [related-doc3](link) +``` + +### Cross-Document Links + +When linking to other documents: + +```markdown + +See [Related Document](../folder/document.md) for more details. + + +For deployment procedures, see the [Deployment Guide](../../other-repo/docs/deployment.md). + + +Refer to [Microsoft Documentation](https://docs.microsoft.com/azure) for Azure-specific guidance. +``` + +**Best Practices:** + +- Use relative paths for same-repository links +- Include context when linking ("See X for Y details" vs "Click here") +- Verify links work before committing +- Update links when files are moved or renamed + +--- + +## Document Types + +All documents must be classified by type to set appropriate expectations and structure. + +| Type | Purpose | Example | Required Sections | +|------|---------|---------|-------------------| +| **RUNBOOK** | Step-by-step procedures for deployment/operations | Provisioning runbooks | Prerequisites, Steps, Validation, Troubleshooting | +| **GUIDE** | Conceptual guidance and best practices | Getting started guides | Overview, Concepts, Best Practices, Examples | +| **REFERENCE** | Technical reference material | API documentation, schemas | Overview, Reference Details, Examples | +| **DESIGN** | Architecture and design decisions | Design documents | Context, Requirements, Design, Rationale | +| **HOWTO** | Task-focused instructions | How to configure X | Objective, Prerequisites, Steps, Verification | +| **STANDARDS** | Standards and conventions | This document | Scope, Standards, Examples, Quality Checklist | + +:::tip Provisioning Runbooks +For step-by-step provisioning and deployment runbooks, see the detailed [Provisioning Runbook Standard](../provisioning/provisioning-runbook-standard.mdx) which defines the stage/step structure, deployment option tabs, and templates. +::: + +### 1. Architecture/Design Documents + +**Purpose**: Explain design decisions and system architecture + +**Required Sections**: + +- Overview +- Requirements +- Architecture diagram +- Component descriptions +- Design decisions and rationale +- Trade-offs and alternatives considered +- Security considerations +- References + +**Example**: `docs/design/network-architecture.md` + +--- + +### 2. How-To Guides + +**Purpose**: Step-by-step procedures for specific tasks + +**Required Sections**: + +- Overview (what you'll accomplish) +- Prerequisites +- Step-by-step instructions +- Validation/testing +- Troubleshooting +- Related guides + +**Format**: + +```markdown +## Prerequisites + +- [ ] Requirement 1 +- [ ] Requirement 2 + +## Steps + +### 1. First Major Step + +Description of what this does. + +```powershell +# Command example +Do-Something -Parameter Value +``` + +**Expected output**: + +``` +Output example +``` + +### 2. Second Major Step + +... + +## Validation + +How to verify success: + +```powershell +Test-Something +``` + +## Troubleshooting + +### Issue 1 + +**Symptom**: ... +**Cause**: ... +**Solution**: ... + +``` + +--- + +### 3. Reference Documentation + +**Purpose**: Detailed specifications and configuration references + +**Required Sections**: +- Overview +- Complete specification (tables) +- Examples +- Related references + +**Example**: Configuration tables, API references, variable lists + +--- + +### 4. Operational Runbooks + +**Purpose**: Day-to-day operational procedures + +**Required Sections**: +- Purpose +- When to use +- Prerequisites +- Detailed procedure +- Rollback procedure +- Escalation process + +--- + +## Documentation Quality Checklist + +Before considering a document complete, verify: + +### Metadata +- [ ] YAML frontmatter with all required fields +- [ ] Document header with category, scope, purpose +- [ ] Appropriate badges (2-5 badges) +- [ ] Version control footer included +- [ ] Status field set correctly + +### Structure +- [ ] Table of contents present and accurate +- [ ] Consistent heading hierarchy (H1 → H2 → H3) +- [ ] Navigation links at major section ends +- [ ] No orphan sections (all linked from TOC) +- [ ] Quick navigation for longer documents + +### Content +- [ ] Clear, concise language +- [ ] Code blocks have language specified +- [ ] All links validated and working +- [ ] Callouts used appropriately +- [ ] Consistent terminology used +- [ ] Examples provided where helpful +- [ ] Spelling & grammar checked +- [ ] Technical content verified + +### Visual Elements +- [ ] Tables have headers +- [ ] Diagrams have alt text and captions +- [ ] Code blocks properly formatted +- [ ] Callout boxes used for important info +- [ ] Lists are properly nested + +### Accessibility +- [ ] Links are descriptive (not "click here") +- [ ] Images have alt text +- [ ] Tables have proper headers +- [ ] Consistent heading hierarchy +- [ ] Sufficient color contrast in diagrams + +### Cross-References +- [ ] Related documents linked +- [ ] Cross-references updated if moving/renaming +- [ ] Master references identified +- [ ] No broken links + +--- + +## Documentation Review Process + +### Self-Review + +Before submitting: +1. Read through completely as if you're the audience +2. Test all commands and procedures +3. Verify all links +4. Check formatting in preview mode + +### Peer Review + +Review checklist: +- [ ] Purpose is clear +- [ ] Content is accurate +- [ ] Steps are reproducible +- [ ] Examples work as described +- [ ] Terminology is consistent +- [ ] Formatting is proper +- [ ] Links are functional + +--- + +## Documentation Organization + +### Cross-Pollination Strategy + +When documenting common procedures: + +**Option 1: Reference Master Documentation** +```markdown +# Cluster Deployment + +For complete deployment procedures, see: +- [Master Deployment Guide](C:\workspaces\knowledge-base\deployment-procedures.md) + +## Environment-Specific Notes + +- For this environment, use: `CLUSTER_NAME=demo-clus01` +- Custom network: VLAN 100 +``` + +**Option 2: Include with Environment Notes** + +```markdown +# Cluster Deployment + +{Standard deployment procedure} + +> **Note**: This is based on the master procedure. See +> [Master Guide](link) for latest updates. + +## Environment-Specific Customizations + +- ... +``` + +--- + +## Writing Style Guide + +### Technical Writing Best Practices + +**Be Clear**: + +- Use simple, direct language +- Define acronyms on first use +- Avoid jargon unless necessary + +**Be Concise**: + +- Get to the point quickly +- Remove unnecessary words +- Use active voice + +**Be Consistent**: + +- Use same terms for same concepts +- Follow naming conventions +- Maintain consistent formatting + +### Common Terminology + +Standardize these terms: + +| Use This | Not This | +|----------|----------| +| Azure Local | Azure Stack HCI, AzStack, ASH | +| cluster | Cluster, CLUSTER | +| node | Node, NODE, server | +| PowerShell | Powershell, powershell, PS | +| Terraform | terraform, TF | + +--- + +## Special Document Types + +### TODO Lists + +**Format**: + +```markdown +# TODO - {Area Name} + +**Last Updated**: YYYY-MM-DD + +## HIGH Priority + +- [ ] **Task name** - Description + - Details + - Owner: {Name} + - Due: YYYY-MM-DD + +## MEDIUM Priority + +... + +## LOW Priority + +... + +## Completed + +- [x] **Completed task** - Completed YYYY-MM-DD +``` + +--- + +### CHANGELOG Format + +Follow [Keep a Changelog](https://keepachangelog.com/): + +```markdown +# Changelog + +## [Unreleased] + +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security + +## [1.0.0] - YYYY-MM-DD + +### Added +- New feature +``` + +--- + +## Documentation Tools + +### Recommended VS Code Extensions + +- **Markdown All in One**: Shortcuts and formatting +- **markdownlint**: Linting and style checking +- **Markdown Preview Enhanced**: Rich preview +- **Code Spell Checker**: Spelling + +### Validation + +```powershell +# Validate markdown files +markdownlint **/*.md + +# Check for broken links +markdown-link-check **/*.md +``` + +--- + +## Documentation Metrics + +Track documentation health: + +| Metric | Target | How to Measure | +|--------|--------|----------------| +| **Coverage** | 100% of features | Every feature has docs | +| **Freshness** | \<90 days | Last updated date | +| **Accuracy** | >95% | Tested procedures work | +| **Completeness** | All sections | Required sections present | +| **Readability** | Grade 8-10 | Readability tools | + +--- + +## Related Standards + +These standards work together to define the complete documentation framework: + +- **[Badge Library](./badge-library.mdx)** - Standard badge definitions and usage +- **[Naming Conventions](./naming-conventions.mdx)** - File and resource naming +- **[DrawIO Diagramming Standard](./drawio-diagramming-standard.mdx)** - Diagram creation standards +- **[Scripting Standards](../scripting/scripting-standards.mdx)** - PowerShell script documentation +- **[Scripting Framework](../scripting/scripting-framework.mdx)** - Script architecture patterns +- **[Provisioning Runbook Standard](../provisioning/provisioning-runbook-standard.mdx)** - Step-by-step deployment guides + +--- + +## External References + +- [Microsoft Style Guide](https://docs.microsoft.com/en-us/style-guide/) +- [Google Developer Documentation Style Guide](https://developers.google.com/style) +- [Keep a Changelog](https://keepachangelog.com/) +- [Semantic Versioning](https://semver.org/) +- [Azure Cloud Adoption Framework](https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/) + +--- + +**Version Control** + +- Created: 2025-12-04 by Product Technology Team +- Last Updated: 2026-02-08 by Product Technology Team +- Version: 2.0.1 +- Tags: standards, documentation, quality, framework, yaml, metadata +- Keywords: documentation, standards, markdown, quality, best-practices, callouts, navigation +- Author: Product Technology Team +- Next Review: 2026-05-08 + diff --git a/standards/documentation/drawio-diagramming-standard.mdx b/standards/documentation/drawio-diagramming-standard.mdx new file mode 100644 index 000000000..9a9d51a00 --- /dev/null +++ b/standards/documentation/drawio-diagramming-standard.mdx @@ -0,0 +1,521 @@ +--- +id: drawio-diagramming-standard +title: Draw.io Diagramming Standard +sidebar_label: Draw.io Diagramming Standard +sidebar_position: 4 +--- +# DrawIO Diagramming Standard + +[![Standards](https://img.shields.io/badge/Standards-Documentation-purple?logo=book)](https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/) +[![DrawIO](https://img.shields.io/badge/DrawIO-Diagrams-orange?logo=diagrams.net)](https://www.drawio.com/) +[![Version-Control](https://img.shields.io/badge/Version-Control-green?logo=git)](https://git-scm.com/) + +> **DOCUMENT CATEGORY**: Standard +> **SCOPE**: Diagramming tool requirements for Azure Local infrastructure documentation +> **PURPOSE**: Establish DrawIO as the mandatory diagramming standard across all repositories +> **MASTER REFERENCE**: [Documentation Standards](./documentation-standards.mdx) + +--- + +## Overview + +This document establishes **DrawIO (.drawio format)** as the **mandatory standard** for all infrastructure diagrams across Product Technology Azure Local environments. This decision prioritizes version control compatibility, documentation integration, and accessibility. + +## Quick Reference + +| Aspect | Requirement | +|--------|-------------| +| **File Format** | `.drawio` (XML-based) | +| **Tool** | [DrawIO (diagrams.net)](https://www.drawio.com/) | +| **Storage Location** | `docs/diagrams/` in each repository | +| **Folder Requirement** | ALL repos must have `docs/diagrams/` (empty if no diagrams) | +| **Prohibited Formats** | Visio (.vsdx), Binary formats | +| **Export Formats** | PNG, SVG (for markdown embedding) | + +--- + +## Why DrawIO? + +### Version Control Friendly + +**DrawIO files are XML-based text files**, making them ideal for Git workflows: + +```xml + + + + + + + + + + + + +``` + +**Benefits**: + +- **Line-by-line diffs** - See exactly what changed in network diagrams +- **Meaningful commit history** - Track topology evolution over time +- **Merge conflict resolution** - Resolve diagram conflicts like code +- **Git blame support** - Identify who changed specific diagram elements + +**Comparison with Visio**: + +``` +Visio (.vsdx): Binary format → Git shows "file changed" only +DrawIO (.drawio): Text XML → Git shows "Added server-2, Changed VLAN 150" +``` + +### Documentation Integration + +**DrawIO diagrams can be embedded directly in markdown documentation**: + +```markdown + +![Network Topology](diagrams/network-topology.drawio.svg) + + +![Network Topology](diagrams/network-topology.png) +``` + +**Advantages**: + +- **Single source of truth** - Diagram source and rendered view in same repo +- **Automatic rendering** - GitHub/GitLab render .drawio.svg files inline +- **Documentation stays current** - Update diagram, export, commit once +- **No external tools required** - View diagrams in web browser directly + +**Visio Alternative** (not recommended): + +``` +Separate Visio file → Must export PNG → Upload to repo → Reference in docs +Updates require 3 steps (edit Visio, export, replace PNG) +Source file often forgotten/outdated +``` + +### Cross-Platform & Free + +**DrawIO is accessible to entire team without licensing barriers**: + +| Platform | Access Method | +|----------|---------------| +| **Web** | [app.diagrams.net](https://app.diagrams.net/) (no login required) | +| **Desktop** | Windows, macOS, Linux (free download) | +| **VS Code** | [Draw.io Integration extension](https://marketplace.visualstudio.com/items?itemName=hediet.vscode-drawio) | +| **Mobile** | iOS/Android (limited editing) | + +**Cost Comparison**: + +- **DrawIO**: $0 (open source, MIT licensed) +- **Visio Plan 2**: $15/user/month ($180/year per person) +- **Lucidchart**: $7.95-$20/user/month + +**Team Benefits**: + +- Zero onboarding friction (no license requests) +- Contractors and customers can view/edit +- No license management overhead +- Consistent tool across all team members + +### VS Code Integration + +**Developers can edit diagrams without leaving VS Code**: + +**Install Extension**: + +```bash +# Via command palette (Ctrl+P) +ext install hediet.vscode-drawio + +# Or install from VS Code marketplace +``` + +**Features**: + +- **Live preview** - See changes as you draw +- **Diff view** - Compare diagram versions side-by-side +- **Integrated workflow** - Edit code and diagrams in same window +- **Custom themes** - Dark mode support +- **Keyboard shortcuts** - Faster diagram creation + +**Workflow Example**: + +1. Open `.drawio` file in VS Code +2. Edit network topology (add server, change VLAN) +3. Export SVG (File → Export as → SVG) +4. Save both `.drawio` and `.svg` +5. Commit both files in single commit + +--- + +## Implementation Requirements + +### Mandatory Directory Structure + +**ALL repositories must include `docs/diagrams/` folder**, even if currently empty: + +``` +{environment-repo}/ +├── docs/ +│ ├── diagrams/ # REQUIRED (empty if no diagrams) +│ │ ├── README.md # Diagram inventory and naming conventions +│ │ ├── network-topology.drawio +│ │ ├── network-topology.drawio.svg +│ │ ├── azure-architecture.drawio +│ │ └── azure-architecture.drawio.svg +│ ├── architecture/ +│ ├── deployment/ +│ └── operations/ +``` + +**Rationale**: + +- **Consistent structure** across all repos +- **Clear expectations** for documentation standards +- **Future-proofing** - Folder exists when diagrams are needed +- **Automated validation** - Scripts can check for folder presence + +### Diagram Naming Conventions + +Follow the [naming conventions standard](./naming-conventions.mdx): + +| Diagram Type | File Name | Description | +|--------------|-----------|-------------| +| **Network Topology** | `network-topology.drawio` | Physical/logical network layout | +| **Azure Architecture** | `azure-architecture.drawio` | Azure resource deployment | +| **Cluster Layout** | `cluster-layout.drawio` | Azure Local cluster physical arrangement | +| **VLAN Configuration** | `vlan-configuration.drawio` | VLAN assignments and routing | +| **Storage Architecture** | `storage-architecture.drawio` | Storage spaces and volumes | +| **Active Directory** | `active-directory-structure.drawio` | Domain/OU hierarchy | + +**Pattern**: `{purpose}-{detail}.drawio` + +- Use lowercase with hyphens +- Be descriptive but concise +- Avoid dates in filename (Git tracks versions) + +### Export Requirements + +**When committing .drawio files, also commit rendered exports for documentation embedding**: + +```bash +# Recommended workflow +1. Create/edit: network-topology.drawio +2. Export SVG: File → Export as → SVG (embedded images) +3. Save as: network-topology.drawio.svg +4. Commit both: git add docs/diagrams/network-topology.drawio* +``` + +**Supported Export Formats**: + +- **SVG** (Recommended): Scalable, small file size, GitHub renders inline +- **PNG**: Raster format, use for high-resolution images +- **PDF**: For printable documentation + +**Export Settings**: + +- **Embed Images**: Check "Embed Images" in export dialog (no external dependencies) +- **Transparent Background**: For clean markdown embedding +- **High DPI**: 2x or 3x scale for PNG exports +- **Include Grid**: Uncheck for cleaner appearance + +--- + +## Prohibited Formats + +### Microsoft Visio (.vsdx) + +**Visio files are NOT allowed** in any repository: + +**Reasons**: + +1. **Binary format** - Git cannot show meaningful diffs +2. **Not embeddable** - Must export PNG separately +3. **Licensing costs** - Not accessible to entire team +4. **Poor version control** - Changes appear as "file modified" +5. **Windows-only** - Linux/macOS users cannot edit + +**Migration Path**: +If you have existing Visio diagrams: + +1. Open in Visio or Visio Online +2. Export as `.vsdx` → Import to DrawIO (File → Import → Visio) +3. Save as `.drawio` format +4. Export SVG for documentation +5. Delete original `.vsdx` file from repository + +### Other Prohibited Formats + +| Format | Reason | +|--------|--------| +| **Lucidchart (.lucid)** | Proprietary, requires subscription | +| **Miro (.rtb)** | Binary format, collaboration-focused (not version control) | +| **PowerPoint (.pptx)** | Binary, not designed for technical diagrams | +| **Gliffy** | Online-only, no offline editing | +| **Binary image files only** | No source diagram (PNG/SVG without .drawio source) | + +**Exception**: PNG/SVG exports are required alongside .drawio source files for documentation embedding. + +--- + +## VS Code Extension Setup + +### Installation + +**Method 1: VS Code Marketplace** + +1. Open VS Code +2. Navigate to Extensions (Ctrl+Shift+X) +3. Search: "Draw.io Integration" +4. Click "Install" on extension by Henning Dieterichs + +**Method 2: Command Line** + +```bash +code --install-extension hediet.vscode-drawio +``` + +**Method 3: Add to `.vscode/extensions.json`** + +```json +{ + "recommendations": [ + "hediet.vscode-drawio", + "ms-vscode.powershell", + "hashicorp.terraform" + ] +} +``` + +### Extension Configuration + +**Recommended settings** for `.vscode/settings.json`: + +```json +{ + "hediet.vscode-drawio.theme": "automatic", + "hediet.vscode-drawio.offline": true, + "hediet.vscode-drawio.enableProposedApi": true, + "hediet.vscode-drawio.customFonts": [], + "hediet.vscode-drawio.customLibraries": [ + { + "libName": "Azure Icons", + "entryId": "azure-official" + } + ] +} +``` + +### Usage + +**Create New Diagram**: + +1. Right-click `docs/diagrams/` folder +2. Select "New File" +3. Name: `network-topology.drawio` +4. DrawIO editor opens automatically + +**Edit Existing Diagram**: + +- Click on `.drawio` file in explorer +- Editor opens with live preview +- Changes save automatically + +**Export from VS Code**: + +1. Open diagram in DrawIO editor +2. Press `Ctrl+Shift+P` → "Draw.io: Export to..." +3. Select format (SVG recommended) +4. Save as `{diagram-name}.drawio.svg` + +--- + +## Diagram Best Practices + +### Azure Local Infrastructure Diagrams + +**Recommended Diagrams for Each Repo**: + +| Repository | Required Diagrams | +|------------|-------------------| +| **All Repos** | `network-topology.drawio`, `azure-architecture.drawio` | +| **Customer Site A** | `switchless-topology.drawio` (2-node direct RDMA) | +| **Customer Site B** | `portable-cluster-layout.drawio`, `conference-network.drawio` | +| **Customer Site C** | `production-cluster-layout.drawio` | +| **ProductLabs** | `datacenter-rack-layout.drawio`, `vlan-configuration.drawio` | + +### Diagram Elements + +**Use Built-in Shape Libraries**: + +- **Azure**: File → Open Library → Azure (official Microsoft icons) +- **Network**: File → Open Library → Network +- **Server**: File → Open Library → Server Hardware + +**Consistent Styling**: + +``` +Servers: Blue boxes (#0078D4) +Network: Green boxes (#107C10) +Storage: Orange boxes (#D83B01) +Security: Red boxes (#E81123) +Cloud: Light blue (#00BCF2) +``` + +**Labeling Standards**: + +- Include IP addresses on network interfaces +- Show VLAN IDs on network segments +- Label all connections (1GbE, 10GbE, 25GbE) +- Include rack/U position for physical servers + +### Version Control Best Practices + +**Commit Messages**: + +```bash +# Good +git commit -m "docs: Update network topology with VLAN 150 for storage traffic" + +# Bad +git commit -m "Updated diagram" +``` + +**Branching**: + +- Create feature branch for major diagram changes +- Review changes via pull request (GitLab/GitHub shows visual diff) +- Merge after team approval + +**Diagram Reviews**: + +- Include `.drawio` AND exported `.svg` in PR +- Reviewers can open `.drawio` source for detailed inspection +- Comment on specific diagram elements in PR discussion + +--- + +## Migration Checklist + +**For Existing Repositories with Visio/Other Formats**: + +- [ ] **Inventory Existing Diagrams** + - [ ] List all `.vsdx`, `.lucid`, `.pptx` diagram files + - [ ] Document diagram purpose and owner + - [ ] Identify outdated diagrams (consider deprecating) + +- [ ] **Create `docs/diagrams/` Folder** + - [ ] Add folder to repository structure + - [ ] Create `README.md` in `docs/diagrams/` with conventions + - [ ] Add `.gitkeep` if folder is initially empty + +- [ ] **Convert Visio Diagrams** + - [ ] Import each `.vsdx` to DrawIO (File → Import → Visio) + - [ ] Review and clean up imported elements + - [ ] Save as `.drawio` format + - [ ] Export SVG for documentation + +- [ ] **Update Documentation References** + - [ ] Find markdown files referencing old diagrams + - [ ] Update image links to new `.drawio.svg` exports + - [ ] Test rendering in GitHub/GitLab preview + +- [ ] **Remove Prohibited Formats** + - [ ] Delete `.vsdx` files from repository + - [ ] Update `.gitignore` to prevent future additions: + + ```gitignore + # Prohibited diagram formats + *.vsdx + *.vsd + *.lucid + ``` + +- [ ] **Install VS Code Extension** + - [ ] Add `hediet.vscode-drawio` to `.vscode/extensions.json` + - [ ] Configure settings in `.vscode/settings.json` + - [ ] Document in repo README + +- [ ] **Team Training** + - [ ] Share this standard document with team + - [ ] Demo DrawIO features in team meeting + - [ ] Create example diagram as template + +--- + +## Resources + +### Official Documentation + +- **DrawIO Website**: [https://www.drawio.com/](https://www.drawio.com/) +- **DrawIO GitHub**: [https://github.com/jgraph/drawio](https://github.com/jgraph/drawio) +- **VS Code Extension**: [https://marketplace.visualstudio.com/items?itemName=hediet.vscode-drawio](https://marketplace.visualstudio.com/items?itemName=hediet.vscode-drawio) + +### Tutorials + +- **Getting Started**: [https://www.drawio.com/doc/getting-started](https://www.drawio.com/doc/getting-started) +- **Shape Libraries**: [https://www.drawio.com/doc/faq/shape-libraries](https://www.drawio.com/doc/faq/shape-libraries) +- **Azure Icons**: [https://learn.microsoft.com/en-us/azure/architecture/icons/](https://learn.microsoft.com/en-us/azure/architecture/icons/) + +### Support + +- **DrawIO Forum**: [https://github.com/jgraph/drawio/discussions](https://github.com/jgraph/drawio/discussions) +- **VS Code Extension Issues**: [https://github.com/hediet/vscode-drawio/issues](https://github.com/hediet/vscode-drawio/issues) + +--- + +## Compliance & Enforcement + +### Automated Checks + +**CI/CD Validation** (future enhancement): + +```powershell +# Check for prohibited formats in PR +$prohibitedFormats = @('*.vsdx', '*.vsd', '*.lucid') +$foundFiles = Get-ChildItem -Recurse -Include $prohibitedFormats + +if ($foundFiles.Count -gt 0) { + Write-Error "Prohibited diagram formats found: $($foundFiles.Name -join ', ')" + exit 1 +} + +# Check for docs/diagrams/ folder +if (-not (Test-Path 'docs/diagrams')) { + Write-Error "Required folder 'docs/diagrams/' not found" + exit 1 +} +``` + +### Manual Review + +**During Pull Request Reviews**: + +- Verify `.drawio` source is included with `.svg` export +- Check diagram follows naming conventions +- Confirm labels and annotations are clear +- Test embedded diagrams render in markdown preview + +### Non-Compliance Remediation + +**If non-compliant diagrams are found**: + +1. Create GitHub issue: "Migrate diagrams to DrawIO standard" +2. Assign to diagram owner or documentation lead +3. Follow migration checklist (above) +4. Close issue when all diagrams converted + +--- + +**Version Control** + +- Created: 2025-12-10 by Product Technology Team +- Last Edited: 2025-12-10 by GitHub Copilot +- Version: 1.0.0 +- Tags: standards, documentation, diagrams, drawio +- Keywords: drawio, diagramming, version-control, documentation, standards +- Author: Product Technology Team + diff --git a/standards/documentation/index.mdx b/standards/documentation/index.mdx new file mode 100644 index 000000000..307b6a63c --- /dev/null +++ b/standards/documentation/index.mdx @@ -0,0 +1,50 @@ +--- +title: Documentation Standards +sidebar_label: Documentation +sidebar_position: 1 +description: "Writing standards, formatting guidelines, naming conventions, and document templates" +--- + +# Documentation Standards + +[![Documentation](https://img.shields.io/badge/Type-Documentation-purple?style=flat-square)](./documentation-standards.mdx) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +> **DOCUMENT CATEGORY**: Standards Index +> **SCOPE**: Writing, formatting, and document organization standards +> **PURPOSE**: Central hub for documentation authoring standards and templates +> **MASTER REFERENCE**: [Documentation Standards](./documentation-standards.mdx) + +**Status**: Active +**Applies To**: All TierPoint Product Technology repositories +**Last Updated**: 2026-01-30 + +--- + +Standards for writing, formatting, and organizing documentation across all TierPoint ProdTech repositories. + +## Overview + +This section covers: + +- **Core writing standards** - Structure, metadata, formatting +- **Naming conventions** - Files, resources, and identifiers +- **Visual elements** - Badges, diagrams, callouts +- **Templates** - Reusable document structures + +## Standards in This Section + +import DocCardList from '@theme/DocCardList'; + + + +## Quick Reference + +| Standard | Purpose | +|----------|---------| +| [Documentation Standards](./documentation-standards.mdx) | Core writing and formatting standards | +| [Naming Conventions](./naming-conventions.mdx) | File and resource naming patterns | +| [Badge Library](./badge-library.mdx) | Standard badge definitions | +| [DrawIO Diagramming](./drawio-diagramming-standard.mdx) | Diagram creation standards | +| [MDX Reference Guide](./mdx-reference-guide.mdx) | MDX syntax and components | +| [Golden Template](./templates/golden-template.mdx) | General document template | diff --git a/standards/documentation/mdx-reference-guide.mdx b/standards/documentation/mdx-reference-guide.mdx new file mode 100644 index 000000000..84fd04d38 --- /dev/null +++ b/standards/documentation/mdx-reference-guide.mdx @@ -0,0 +1,169 @@ +--- +title: MDX Reference Guide +sidebar_label: MDX Reference Guide +sidebar_position: 5 +description: "Quick reference for MDX syntax, components, and patterns used in TierPoint documentation" +--- + +# MDX Reference Guide + +[![Reference](https://img.shields.io/badge/Type-Reference-purple?style=flat-square)](./documentation-standards.mdx) +[![MDX](https://img.shields.io/badge/Format-MDX-blue?style=flat-square)](https://mdxjs.com/) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +> **DOCUMENT CATEGORY**: Reference +> **SCOPE**: MDX syntax and components for Docusaurus documentation +> **PURPOSE**: Quick reference for common MDX patterns and features +> **MASTER REFERENCE**: [Documentation Standards](./documentation-standards.mdx) + +**Status**: Active +**Applies To**: All TierPoint Product Technology documentation +**Last Updated**: 2026-01-30 + +--- + +This guide provides examples of common MDX (Markdown + JSX) features and components used in TierPoint documentation. Use these patterns to ensure consistency and clarity across all standards documents. + +--- + +## Headings + +```mdx +# H1 Heading +## H2 Heading +### H3 Heading +``` + +--- + +## Paragraphs and Line Breaks + +Separate paragraphs with a blank line. Use `
` for a line break within a paragraph. + +--- + +## Bold, Italic, and Inline Code + +```mdx +**Bold text** +*Italic text* +`Inline code` +``` + +--- + +## Code Blocks + +
+Example + +````mdx +```bash +az login +az group create --name myGroup --location eastus +``` +```` + +
+ +--- + +## Admonitions (Callout Boxes) + +Docusaurus supports special callout/admonition blocks for notes, tips, warnings, etc. + +```mdx +:::note +This is a note. +::: + +:::tip +This is a tip. +::: + +:::info +This is an info box. +::: + +:::caution +This is a caution. +::: + +:::danger +This is a danger/warning. +::: +``` + +--- + +## Lists + +```mdx +- Unordered list item +- Another item + +1. Ordered list item +2. Another item +``` + +--- + +## Links and Images + +```mdx +[Link text](https://example.com) + +![Alt text](../img/example.png) +``` + +--- + +## Tables + +```mdx +| Column 1 | Column 2 | +|----------|----------| +| Value 1 | Value 2 | +``` + +--- + +## Blockquotes + +```mdx +> This is a blockquote. +``` + +--- + +## Custom Components + +You can use React components if needed (advanced usage): + +```mdx + +``` + +--- + +## Horizontal Rule + +```mdx +--- +``` + +--- + +## Escaping Markdown + +Use a backslash `\` to escape special characters. + +--- + +## More Resources +- [Docusaurus Markdown Features](https://docusaurus.io/docs/markdown-features) +- [MDX Syntax Reference](https://mdxjs.com/docs/syntax/) + +--- + +*Data verified with Docusaurus documentation release note dated 2026-01-29.* diff --git a/standards/documentation/naming-conventions.mdx b/standards/documentation/naming-conventions.mdx new file mode 100644 index 000000000..16cc159d1 --- /dev/null +++ b/standards/documentation/naming-conventions.mdx @@ -0,0 +1,826 @@ +--- +id: naming-conventions +title: Naming Conventions +sidebar_label: Naming Conventions +sidebar_position: 2 +--- + +# Naming Conventions + +[![Standards](https://img.shields.io/badge/Type-Standard-purple?style=flat-square)](./documentation-standards.mdx) +[![Naming](https://img.shields.io/badge/Category-Naming-blue?style=flat-square)](./documentation-standards.mdx) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +> **DOCUMENT CATEGORY**: Standard +> **SCOPE**: Unified naming conventions for all TierPoint infrastructure platforms +> **PURPOSE**: Establish consistent naming standards across Azure, VMware, Hyper-V, networking, and variables +> **MASTER REFERENCE**: [Documentation Standards](./documentation-standards.mdx) + +**Status**: Active +**Applies To**: All TierPoint Product Technology repositories +**Last Updated**: 2026-01-30 +**Version**: 3.1.0 +**Sources**: Variable Management Registry, Azure CAF/WAF, Networking Standards, Infrastructure Patterns + +--- + +## Purpose + +This document establishes unified naming conventions across all TierPoint Product Technology infrastructure platforms and repositories. It consolidates standards for VMware, Hyper-V, networking, firewalls, file/directory naming, and variable naming following the master variable registry specifications. + +**Consolidates standards for**: + +- File and directory structures +- Variable naming (following master registry snake_case standards) +- VMware vSphere naming (clusters, hosts, VMs, datastores) +- Hyper-V naming (hosts, VMs, virtual switches) +- Azure Local naming (clusters, nodes, resource groups) +- Azure Virtual Desktop naming (host pools, workspaces, session hosts) +- Networking naming (VLANs, subnets, firewalls, switches) +- Git workflow conventions + +**Core Principles**: + +1. **Consistency** - Use established patterns across all platforms +2. **Clarity** - Names should be self-explanatory and descriptive +3. **Compliance** - Follow platform-specific naming rules and character limits +4. **Maintainability** - Enable easy navigation and automation +5. **Registry Alignment** - Variables follow master registry technical standards + +--- + +## File and Directory Naming + +### General Rules + +| Type | Convention | Pattern | Notes | +|------|-----------|---------|-------| +| **Directories** | lowercase-with-hyphens | `^[a-z][a-z0-9-]*$` | Use descriptive names, max 50 chars | +| **Markdown Files** | `README.md` (uppercase) | `README.md`, `CHANGELOG.md`, `TODO.md` | Standard across all repos | +| **Configuration Files** | lowercase-with-hyphens.ext | `^[a-z][a-z0-9-]*\.[a-z]+$` | Use meaningful extensions | +| **PowerShell Scripts** | `PascalCase.ps1` | `^[A-Z][a-Za-z0-9]*\.ps1$` | Follow PowerShell conventions | +| **Python Scripts** | lowercase_with_underscores.py | `^[a-z][a-z0-9_]*\.py$` | Follow Python PEP 8 | +| **Shell Scripts** | lowercase-with-hyphens.sh | `^[a-z][a-z0-9-]*\.sh$` | Unix convention | + +### Special Cases + +#### Root-Level Files + +**MUST be uppercase**: + +- `README.md` (primary repository documentation) +- `CHANGELOG.md` (version history) +- `TODO.md` (task tracking when in root) +- `CONTRIBUTING.md` (contribution guidelines) +- `LICENSE` (license file) +- `CODEOWNERS` (code ownership) + +#### Documentation Files + +**In `docs/` directory**: + +- `README.md` (uppercase) for directory indexes +- `*.md` (lowercase with hyphens) for all other docs + +--- + +## Directory Structure Naming + +### Standard Directories (Required) + +```text +repository-root/├── .github/ # GitHub-specific files (lowercase) +├── .vscode/ # VS Code settings (lowercase) +├── config/ # Configuration files (lowercase) +├── docs/ # Documentation (lowercase) +│ ├── configuration/ # Generated config docs +│ ├── design/ # Architecture/design +│ ├── diagrams/ # DrawIO diagrams (.drawio) +│ ├── guides/ # How-to guides +│ └── operations/ # Runbooks +├── src/ # Source code (lowercase) +│ ├── powershell/ +│ ├── terraform/ +│ ├── bicep/ +│ ├── ansible/ +│ └── azurecli/ +├── tests/ # Test files (lowercase) +├── tools/ # Utility tools (lowercase) +├── logs/ # Log files (lowercase, gitignored) +├── CHANGELOG.md # Version history (UPPERCASE) +├── README.md # Repository documentation (UPPERCASE) +└── TODO.md # Task tracking (UPPERCASE) +``` + +### Sub-directory Naming + +**Use descriptive, lowercase names with hyphens**: + +**Good Examples**: + +```text +docs/ +├── configuration/ +├── deployment-guides/ +├── network-design/ +└── troubleshooting/ + +src/ +├── powershell/ +├── terraform/ +├── ansible/ +└── azure-cli/ +``` + +**Bad Examples**: + +```text +docs/ +├── Config/ # Don't use mixed case +├── deployment_guides/ # Don't use underscores +├── NetworkDesign/ # Don't use PascalCase +└── Troubleshooting/ # Don't capitalize +``` + +--- + +## Configuration File Naming + +### Environment Configuration + +| Format | Convention | Pattern | +|--------|-----------|---------| +| **.env** | `.env`, `.env.example` | `^\.env(\.example)?$` | +| **YAML/YML** | `lowercase-with-hyphens.yml` | `^[a-z][a-z0-9-]*\.yml$` | +| **JSON** | `lowercase-with-hyphens.json` | `^[a-z][a-z0-9-]*\.json$` | +| **Excel** | `lowercase-with-hyphens.xlsx` | `^[a-z][a-z0-9-]*\.xlsx$` | +| **CSV** | `lowercase-with-hyphens.csv` | `^[a-z][a-z0-9-]*\.csv$` | + +### Infrastructure as Code + +| Tool | Convention | Pattern | +|------|-----------|---------| +| **Terraform** | `lowercase.tf` | `^[a-z][a-z0-9_]*\.tf$` | +| **Bicep** | `lowercase-with-hyphens.bicep` | `^[a-z][a-z0-9-]*\.bicep$` | +| **Ansible** | `lowercase-with-hyphens.yml` | `^[a-z][a-z0-9-]*\.yml$` | + +--- + +## Variable Naming + +### Master Registry Standards + +All variables MUST follow the master variable registry technical standards: + +**Format**: `snake_case` (lowercase with underscores) +**Pattern**: `^[a-z][a-z0-9_]*$` +**Maximum Length**: 50 characters +**Requirements**: Descriptive, unambiguous names + +### Environment Variables + +**Use UPPERCASE_SNAKE_CASE**: + +**Good Examples**: + +```bash +AZURE_SUBSCRIPTION_ID="12345678-1234-1234-1234-123456789012" +CLUSTER_NAME="iic-clus01" +NODE_01_IP="192.168.1.10" +ADMIN_USERNAME="admin" +MGMT_VLAN_ID="711" +``` + +**Bad Examples**: + +```bash +azureSubscriptionId="..." # Don't use camelCase +cluster-name="..." # Don't use lowercase with hyphens +Node01IP="..." # Don't mix cases +``` + +### PowerShell Variables + +**Use PascalCase for local variables**: + +**Good Examples**: + +```powershell +$ClusterName = "iic-clus01" +$SubscriptionId = "12345678-1234-1234-1234-123456789012" +$ResourceGroupName = "rg-azlocal-prod-eus-001" +$NodeCount = 4 +``` + +### Terraform Variables + +**Use lowercase_snake_case**: + +**Good Examples**: + +```hcl +variable "resource_group_name" { + type = string +} + +variable "location" { + type = string +} + +variable "cluster_name" { + type = string +} +``` + +### Registry Variable Examples + +Following master registry patterns: + +```yaml +# Site variables (site category) +site_code: `ral` # 2-5 lowercase letters +site_name: "Raleigh Data Center" +site_datacenter: "TierPoint RAL" + +# Cluster variables (clusters category) +azure_local_name: "ral-clus01" # Max 15 chars for NetBIOS +azure_local_node_count: 4 + +# Network variables (network_intents category) +management_subnet_cidr: "192.168.1.0/24" +management_gateway: "192.168.1.1" +``` + +--- + +## Azure Resource Naming + +:::info Authoritative Source +For complete Azure resource naming standards — including CAF patterns, resource type abbreviations, management groups, VM dual-naming, Entra ID groups, PIM-eligible groups, and all Azure-specific constraints — refer to the master naming standard: + +👉 **[Azure Local Naming Standards](../../product-azure-local-anywhere/planning/01-naming-standards.mdx)** + +This document covers non-Azure platforms (VMware, Hyper-V, networking, firewalls) and shared conventions (files, directories, variables). Azure resource naming is maintained in a single source of truth to prevent drift between documents. +::: + +--- + +## VMware vSphere Naming + +### Cluster Naming + +**Pattern**: `{workload}-{environment}-{datacenter}-{purpose}-{sequence}` + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| workload | Business function | `^[a-z]{2,8}$` | `azlc`, `iic`, `demo` | +| environment | Environment type | `^[a-z]{3,8}$` | `prod`, `dev`, `test` | +| datacenter | Site/datacenter code | `^[a-z]{2,5}$` | `ral`, `atl`, `chi` | +| purpose | Cluster purpose | `^[a-z]{2,8}$` | `mgmt`, `comp`, `storage` | +| sequence | Sequential number | `^[0-9]{2}$` | `01`, `02`, `03` | + +**Examples**: +- `azlc-prod-ral-mgmt-01` (Azure Local production management cluster) +- `iic-dev-atl-comp-01` (Phoenix development compute cluster) +- `demo-test-chi-storage-01` (Demo test storage cluster) + +### ESXi Host Naming + +**Pattern**: `{workload}-{environment}-{datacenter}-{cluster}-{host-number}` + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| workload | Business function | `^[a-z]{2,8}$` | `azlc`, `iic`, `demo` | +| environment | Environment type | `^[a-z]{3,8}$` | `prod`, `dev`, `test` | +| datacenter | Site/datacenter code | `^[a-z]{2,5}$` | `ral`, `atl`, `chi` | +| cluster | Cluster identifier | `^[a-z]{2,8}$` | `mgmt`, `comp` | +| host-number | Host sequence | `^[0-9]{2}$` | `01`, `02`, `03` | + +**Examples**: +- `azlc-prod-ral-mgmt-01` (Management host 1) +- `iic-dev-atl-comp-02` (Compute host 2) +- `demo-test-chi-storage-03` (Storage host 3) + +### Virtual Machine Naming + +**Pattern**: `{workload}-{environment}-{datacenter}-{purpose}-{sequence}` + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| workload | Business function | `^[a-z]{2,8}$` | `azlc`, `iic`, `demo` | +| environment | Environment type | `^[a-z]{3,8}$` | `prod`, `dev`, `test` | +| datacenter | Site/datacenter code | `^[a-z]{2,5}$` | `ral`, `atl`, `chi` | +| purpose | VM purpose | `^[a-z]{2,8}$` | `web`, `app`, `db`, `mgmt` | +| sequence | Sequential number | `^[0-9]{3}$` | `001`, `002`, `003` | + +**Examples**: +- `azlc-prod-ral-web-001` (Production web server 1) +- `iic-dev-atl-app-002` (Development app server 2) +- `demo-test-chi-db-003` (Test database server 3) + +### Datastore Naming + +**Pattern**: `{workload}-{environment}-{datacenter}-{type}-{capacity}-{sequence}` + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| workload | Business function | `^[a-z]{2,8}$` | `azlc`, `iic`, `demo` | +| environment | Environment type | `^[a-z]{3,8}$` | `prod`, `dev`, `test` | +| datacenter | Site/datacenter code | `^[a-z]{2,5}$` | `ral`, `atl`, `chi` | +| type | Storage type | `^[a-z]{2,8}$` | `ssd`, `hdd`, `nvme` | +| capacity | Storage capacity | `^[0-9]{1,4}[tg]$` | `1t`, `2t`, `500g` | +| sequence | Sequential number | `^[0-9]{2}$` | `01`, `02`, `03` | + +**Examples**: +- `azlc-prod-ral-ssd-2t-01` (Production SSD datastore 2TB) +- `iic-dev-atl-hdd-10t-02` (Development HDD datastore 10TB) +- `demo-test-chi-nvme-1t-01` (Test NVMe datastore 1TB) + +--- + +## Hyper-V Naming + +### Host Naming + +**Pattern**: `{site}-{role}{sequence}` (⚠️ Strict 15-char NetBIOS limit) + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| site | Site code from registry | `^[a-z]{2,5}$` | `ral`, `atl` | +| role | Host role abbreviation | `^[a-z]{2,4}$` | `hv`, `cl` | +| sequence | Sequential number | `^[0-9]{2}$` | `01`, `02` | + +**Examples**: +- `ral-hv01` (Raleigh Hyper-V host 1 - 8 chars) +- `atl-cl02` (Atlanta Cluster node 2 - 8 chars) +- `chi-hv03` (Chicago Hyper-V host 3 - 8 chars) + +### Virtual Machine Naming + +**Pattern**: `{workload}-{environment}-{datacenter}-{purpose}-{sequence}` + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| workload | Business function | `^[a-z]{2,8}$` | `azlc`, `iic`, `demo` | +| environment | Environment type | `^[a-z]{3,8}$` | `prod`, `dev`, `test` | +| datacenter | Site/datacenter code | `^[a-z]{2,5}$` | `ral`, `atl`, `chi` | +| purpose | VM purpose | `^[a-z]{2,8}$` | `web`, `app`, `db`, `mgmt` | +| sequence | Sequential number | `^[0-9]{3}$` | `001`, `002`, `003` | + +**Examples**: +- `azlc-prod-ral-web-001` (Production web server 1) +- `iic-dev-atl-app-002` (Development app server 2) +- `demo-test-chi-db-003` (Test database server 3) + +### Virtual Switch Naming + +**Pattern**: `{workload}-{environment}-{datacenter}-{type}-{network}-{sequence}` + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| workload | Business function | `^[a-z]{2,8}$` | `azlc`, `iic`, `demo` | +| environment | Environment type | `^[a-z]{3,8}$` | `prod`, `dev`, `test` | +| datacenter | Site/datacenter code | `^[a-z]{2,5}$` | `ral`, `atl`, `chi` | +| type | Switch type | `^[a-z]{2,8}$` | `ext`, `int`, `priv` | +| network | Network type | `^[a-z]{2,8}$` | `mgmt`, `prod`, `dmz` | +| sequence | Sequential number | `^[0-9]{2}$` | `01`, `02`, `03` | + +**Examples**: +- `azlc-prod-ral-ext-mgmt-01` (External management switch) +- `iic-dev-atl-int-prod-02` (Internal production switch) +- `demo-test-chi-priv-dmz-01` (Private DMZ switch) + +--- + +## Azure Local Naming + +### Cluster Naming + +**Pattern**: `-clus` (⚠️ Strict 15-char NetBIOS limit for CNO) + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| site | Site code from registry | `^[a-z]{2,5}$` | `ral`, `atl` | +| constant | Cluster identifier | `clus` | `clus` | +| sequence | Sequential number | `^[0-9]{2}$` | `01`, `02` | + +**Examples**: +- `ral-clus01` (Raleigh Cluster 1 - 10 chars) +- `atl-clus01` (Atlanta Cluster 1 - 10 chars) + +### Node Naming + +**Pattern**: `--N` (⚠️ Strict 15-char NetBIOS limit) + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| site | Site code from registry | `^[a-z]{2,5}$` | `ral`, `atl` | +| cluster# | Cluster number | `^[0-9]{2}$` | `01`, `02` | +| constant | Node identifier | `N` | `N` | +| node# | Node sequence | `^[0-9]{2}$` | `01`, `02` | + +**Examples**: +- `ral-01-n01` (Raleigh Cluster 01, Node 1 - 10 chars) +- `ral-01-n02` (Raleigh Cluster 01, Node 2 - 10 chars) +- `atl-01-n01` (Atlanta Cluster 01, Node 1 - 10 chars) + +### Management Infrastructure + +**Pattern**: `--` (⚠️ 15-char limit for domain-joined servers) + +**Examples**: +- `ral-dc-01`, `ral-dc-02` (Domain Controllers) +- `ral-ca-01` (Certificate Authority) +- `ral-wac-01` (WAC Gateway) +- `ral-tools` (Tools/Jumpbox Server) + +### Hardware Naming + +**Pattern**: `--<##>` (Consistent pattern for all physical devices) + +| Device | Pattern | Example | +|--------|---------|---------| +| iDRAC/BMC | `-i` | `ral-01-n01-i` | +| ToR Switch | `-sw-<##>` | `ral-sw-01` | +| Firewall | `-fw-<##>` | `ral-fw-01` | +| Opengear | `-og-<##>` | `ral-og-01` | +| PDU | `-pdu-<##>` | `ral-pdu-01` | + +### Resource Group Naming + +**CAF Pattern**: `rg----` + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| prefix | Resource group prefix | `^rg-` | `rg-` | +| workload | Application or service | `^[a-z]{2,8}$` | `azlocal`, `avd` | +| environment | Environment type | `^[a-z]{3,8}$` | `prod`, `dev`, `test` | +| region | Azure region code | `^[a-z]{3,6}$` | `eus`, `eastus`, `wus` | +| instance | Sequential or purpose | `^[a-z0-9]{2,8}$` | `001`, `arc`, `network` | + +**Examples**: +- `rg-azlocal-prod-eus-001` (Primary cluster resources) +- `rg-azlocal-prod-eus-arc` (Arc-connected resources) +- `rg-azlocal-prod-eus-network` (Networking resources) + +--- + +## Azure Virtual Desktop Naming + +### Host Pool Naming + +**Pattern**: `{workload}-{environment}-{region}-{purpose}-{sequence}` + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| workload | Business function | `^[a-z]{2,8}$` | `azlc`, `iic`, `demo` | +| environment | Environment type | `^[a-z]{3,8}$` | `prod`, `dev`, `test` | +| region | Azure region | `^[a-z]{3,4}$` | `eus`, `wus`, `cus` | +| purpose | Pool purpose | `^[a-z]{2,8}$` | `desktop`, `remoteapp` | +| sequence | Sequential number | `^[0-9]{2}$` | `01`, `02`, `03` | + +**Examples**: +- `azlc-prod-eus-desktop-01` (Production desktop pool) +- `iic-dev-wus-remoteapp-02` (Development RemoteApp pool) +- `demo-test-cus-desktop-01` (Test desktop pool) + +### Workspace Naming + +**Pattern**: `{workload}-{environment}-{region}-{workspace}-{sequence}` + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| workload | Business function | `^[a-z]{2,8}$` | `azlc`, `iic`, `demo` | +| environment | Environment type | `^[a-z]{3,8}$` | `prod`, `dev`, `test` | +| region | Azure region | `^[a-z]{3,4}$` | `eus`, `wus`, `cus` | +| workspace | Workspace type | `^[a-z]{2,8}$` | `workspace` | +| sequence | Sequential number | `^[0-9]{2}$` | `01`, `02`, `03` | + +**Examples**: +- `azlc-prod-eus-workspace-01` (Production workspace) +- `iic-dev-wus-workspace-02` (Development workspace) +- `demo-test-cus-workspace-01` (Test workspace) + +### Session Host Naming + +**Pattern**: `{prefix}-{sequence}` (⚠️ Strict 15-char NetBIOS limit) + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| prefix | Host pool prefix | `^[a-zA-Z0-9-]{3,11}$` | `vd-avd`, `vd-azl` | +| sequence | Sequential number | `^[0-9]{2,3}$` | `01`, `001` | + +**Examples**: +- `vd-avd-01` (Azure AVD host 1 - 9 chars) +- `vd-azl-002` (Azure Local AVD host 2 - 10 chars) + +--- + +## Networking Naming + +### VLAN Naming + +**Pattern**: `{site-code}-{purpose}-{sequence}` + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| site-code | Site identifier | `^[a-z]{2,5}$` | `ral`, `atl`, `chi` | +| purpose | VLAN purpose | `^[a-z]{2,8}$` | `mgmt`, `prod`, `dmz`, `storage` | +| sequence | VLAN ID | `^[0-9]{3,4}$` | `100`, `200`, `0711` | + +**Examples**: +- `ral-mgmt-0711` (Raleigh management VLAN 711) +- `atl-prod-0100` (Atlanta production VLAN 100) +- `chi-dmz-0200` (Chicago DMZ VLAN 200) + +### Subnet Naming + +**Pattern**: `{site-code}-{purpose}-{subnet-type}-{sequence}` + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| site-code | Site identifier | `^[a-z]{2,5}$` | `ral`, `atl`, `chi` | +| purpose | Subnet purpose | `^[a-z]{2,8}$` | `mgmt`, `prod`, `dmz` | +| subnet-type | Subnet type | `^[a-z]{2,8}$` | `subnet` | +| sequence | Sequential number | `^[0-9]{2}$` | `01`, `02`, `03` | + +**Examples**: +- `ral-mgmt-subnet-01` (Raleigh management subnet 1) +- `atl-prod-subnet-02` (Atlanta production subnet 2) +- `chi-dmz-subnet-01` (Chicago DMZ subnet 1) + +### Switch Naming + +**Pattern**: `{site-code}-{switch-type}-{sequence}` + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| site-code | Site identifier | `^[a-z]{2,5}$` | `ral`, `atl`, `chi` | +| switch-type | Switch type | `^[a-z]{2,8}$` | `core`, `access`, `tor` | +| sequence | Sequential number | `^[0-9]{2}$` | `01`, `02`, `03` | + +**Examples**: +- `ral-core-01` (Raleigh core switch 1) +- `atl-access-02` (Atlanta access switch 2) +- `chi-tor-03` (Chicago top-of-rack switch 3) + +--- + +## Firewall Naming + +### Firewall Rule Naming + +**Pattern**: `{site-code}-{purpose}-{direction}-{sequence}` + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| site-code | Site identifier | `ral`, `atl`, `chi` | +| purpose | Rule purpose | `^[a-z]{2,8}$` | `https`, `rdp`, `ssh`, `icmp` | +| direction | Traffic direction | `^[a-z]{2,8}$` | `inbound`, `outbound` | +| sequence | Sequential number | `^[0-9]{3}$` | `001`, `002`, `003` | + +**Examples**: +- `ral-https-inbound-001` (Raleigh HTTPS inbound rule 1) +- `atl-rdp-outbound-002` (Atlanta RDP outbound rule 2) +- `chi-ssh-inbound-003` (Chicago SSH inbound rule 3) + +### Firewall Policy Naming + +**Pattern**: `{site-code}-{environment}-{policy-type}-{sequence}` + +| Component | Description | Pattern | Examples | +|-----------|-------------|---------|----------| +| site-code | Site identifier | `ral`, `atl`, `chi` | +| environment | Environment type | `^[a-z]{3,8}$` | `prod`, `dev`, `test` | +| policy-type | Policy type | `^[a-z]{2,8}$` | `base`, `app`, `db` | +| sequence | Sequential number | `^[0-9]{2}$` | `01`, `02`, `03` | + +**Examples**: +- `ral-prod-base-01` (Raleigh production base policy) +- `atl-dev-app-02` (Atlanta development app policy) +- `chi-test-db-01` (Chicago test database policy) + +--- + +## ⚠️ Platform-Specific Naming Limits and Constraints + +### Azure Resource Naming Limits + +| Resource Type | Limit | Valid Characters | Notes | +|--------------|-------|------------------|-------| +| **Key Vault** | 3-24 chars | Alphanumeric, hyphens | **NO consecutive hyphens** | +| **Storage Account** | 3-24 chars | Lowercase letters, numbers | NO hyphens or special chars | +| **Function App** | 2-60 chars | Alphanumeric, hyphens | Must start with letter/number | +| **App Service** | 2-60 chars | Alphanumeric, hyphens | Must start with letter/number | +| **Application Insights** | 1-255 chars | Alphanumeric, hyphens, periods, underscores | - | +| **Log Analytics** | 4-63 chars | Alphanumeric, hyphens | Start and end with alphanumeric | +| **Virtual Machine** | 1-15 (Win), 1-64 (Linux) | Alphanumeric, hyphens | - | +| **Resource Group** | 1-90 chars | Alphanumeric, hyphens, underscores, periods, parentheses | - | +| **SQL Managed Instance** | 1-63 chars | Lowercase alphanumeric, hyphens | No leading/trailing hyphen | + +### VMware vSphere Limits + +| Resource Type | Limit | Valid Characters | Notes | +|--------------|-------|------------------|-------| +| **Cluster Name** | 1-80 chars | Alphanumeric, hyphens, underscores | - | +| **ESXi Hostname** | 1-64 chars | Alphanumeric, hyphens | - | +| **VM Name** | 1-80 chars | Alphanumeric, hyphens, underscores | - | +| **Datastore Name** | 1-42 chars | Alphanumeric, hyphens, underscores | - | + +### Hyper-V Limits + +| Resource Type | Limit | Valid Characters | Notes | +|--------------|-------|------------------|-------| +| **VM Name** | 1-64 chars | Any except: \ / : * ? " < > \| | - | +| **Virtual Switch Name** | 1-64 chars | Any except: \ / : * ? " < > \| | - | +| **Host Name** | 1-15 chars (NetBIOS) | Alphanumeric, hyphens | - | + +### Networking Limits + +| Resource Type | Limit | Valid Characters | Notes | +|--------------|-------|------------------|-------| +| **VLAN Name** | 1-32 chars | Alphanumeric, hyphens | - | +| **Subnet Name** | 1-80 chars | Alphanumeric, hyphens | - | +| **Switch Hostname** | 1-32 chars | Alphanumeric, hyphens | - | + +--- + +## Validation Rules and Patterns + +### Variable Validation + +All variables must follow master registry patterns: + +```yaml +# String validation +site_code: + type: string + maxLength: 5 + pattern: "^[a-z]{2,5}$" + example: "AZL" + +# Integer validation +node_count: + type: integer + minimum: 1 + maximum: 16 + default: 2 + +# Array validation +dns_servers: + type: array + items: + type: string + format: ipv4 + minItems: 1 + maxItems: 4 +``` + +### Resource Name Validation + +**Azure Resource Validation**: +- Key Vault: No consecutive hyphens +- Storage Account: No hyphens at all +- Globally unique resources: Key Vault, Storage Account, SQL MI + +**Windows/NetBIOS Validation (Strict 15-char Limit)**: +- **Hyper-V Hosts**: Must be ≤ 15 chars (e.g. `ral-hv01`) +- **Azure Local Nodes**: Must be ≤ 15 chars (e.g. `ral-01-n01`) +- **Azure Local Cluster Object**: Must be ≤ 15 chars (e.g. `ral-clus01`) +- **Windows VMs**: Must be ≤ 15 chars +- **AVD Session Hosts**: Must be ≤ 15 chars + +**VMware Validation**: +- Cluster names: 80 characters max +- ESXi hostnames: 64 characters max +- VM names: 80 characters max + +**Networking Validation**: +- VLAN IDs: 1-4094 +- IP addresses: Valid IPv4 format +- Subnet masks: Valid CIDR notation + +--- + +## What to Avoid + +### Anti-Patterns + +**Don't**: + +- Mix naming conventions within the same platform +- Use inconsistent capitalization (`README.md` vs `readme.md`) +- Use spaces in any resource names +- Use special characters except hyphens and underscores (where appropriate) +- Use undocumented abbreviations +- Create overly long names (>50 characters for variables, check platform limits for resources) +- Use consecutive hyphens in Key Vault names +- Use hyphens in Storage Account names +- Use generic variable names (`name`, `type`, `config`) +- Exceed platform-specific character limits + +**Do**: + +- Be consistent across all platforms and resources +- Use descriptive names following master registry standards +- Follow tool-specific conventions (PowerShell PascalCase, Terraform snake_case) +- Document any exceptions +- Keep names clear and concise +- Validate against platform naming limits before deployment +- Use snake_case for all variables (max 50 chars) +- Reference master variable registry for authoritative definitions + +--- + +## Compliance Checklist + +Use this checklist to verify naming compliance: + +- [ ] All directories use lowercase-with-hyphens +- [ ] `README.md` files use uppercase in all locations +- [ ] PowerShell scripts use `PascalCase.ps1` +- [ ] Configuration files use lowercase-with-hyphens.ext +- [ ] Environment variables use `UPPERCASE_SNAKE_CASE` +- [ ] PowerShell variables use `PascalCase` +- [ ] Terraform variables use `lowercase_snake_case` +- [ ] Variables follow master registry snake_case (max 50 chars) +- [ ] No spaces in any filenames or resource names +- [ ] Consistent naming within each platform +- [ ] Azure resources follow CAF naming standards +- [ ] VMware resources follow vSphere naming patterns +- [ ] Hyper-V resources follow Windows naming patterns +- [ ] Networking resources follow VLAN/subnet conventions +- [ ] Firewall resources follow security naming patterns +- [ ] Azure resource names validated against length limits +- [ ] Key Vault names have NO consecutive hyphens +- [ ] Storage account names have NO hyphens at all +- [ ] VMware names within character limits +- [ ] Hyper-V names within character limits +- [ ] Tag values are under 256 characters +- [ ] Globally unique names verified (Key Vault, Storage, SQL MI) + +--- + +## Troubleshooting + +### Common Naming Issues + +**Issue**: Key Vault deployment fails with "Invalid name" +**Solution**: Check for consecutive hyphens and ensure 3-24 characters + +**Issue**: Storage account deployment fails with "Invalid name" +**Solution**: Remove all hyphens, use only lowercase letters and numbers + +**Issue**: VM deployment fails with "Name too long" +**Solution**: Windows VMs limited to 15 characters for NetBIOS compatibility + +**Issue**: VMware cluster creation fails +**Solution**: Ensure cluster name is 80 characters or less + +**Issue**: Variable validation fails +**Solution**: Check snake_case format, 50-character limit, and registry patterns + +**Issue**: Resource group naming conflicts +**Solution**: Use CAF naming: `rg----` + +### Exception Process + +In rare cases where customer requirements dictate deviations from standards: + +1. Document the exception with business justification +2. Get approval from ProdTech architecture team +3. Update naming documentation with exception +4. Implement with clear comments explaining deviation + +--- + +## References + +- [Azure Local Naming Standards](../../product-azure-local-anywhere/planning/01-naming-standards.mdx) - Authoritative Azure resource naming +- [Master Variable Registry](../variable-management/registry-reference.mdx) - Authoritative variable definitions +- [Variable Management Technical Standards](../variable-management/technical-standards.mdx) - Naming rules and validation +- [Microsoft Azure CAF - Resource Naming](https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/naming-and-tagging) +- [Azure CAF - Abbreviation Examples](https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +- [Azure Naming Rules and Restrictions](https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/resource-name-rules) +- [VMware vSphere Naming Best Practices](https://docs.vmware.com/en/VMware-vSphere/index.html) +- [PowerShell Approved Verbs](https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands) +- [Python PEP 8 Style Guide](https://www.python.org/dev/peps/pep-0008/) +- [Terraform Style Guide](https://www.terraform.io/docs/language/syntax/style.html) + +--- + +## Related Standards + +- [Variable Management](../variable-management/index.mdx) - Master registry and governance +- [Documentation Standards](./documentation-standards.mdx) - Markdown formatting + +--- + +**Version Control** + +- Created: 2025-12-04 (original), 2025-12-09 (consolidated), 2026-01-29 (comprehensive) +- Last Updated: 2026-01-30 +- Version: 3.1.0 +- Next Review: 2026-07-29 +- Sources: Variable Management Registry, Azure CAF/WAF, Networking Standards, Infrastructure Patterns +- Issue: [#1 - Consolidate Naming Standards Documentation](https://github.com/kristopherjturner/master-workplace-project/issues/1) diff --git a/standards/documentation/templates/golden-index-template.mdx b/standards/documentation/templates/golden-index-template.mdx new file mode 100644 index 000000000..299d161ea --- /dev/null +++ b/standards/documentation/templates/golden-index-template.mdx @@ -0,0 +1,56 @@ +--- +title: "Golden Index Template" +sidebar_label: "Index Template" +sidebar_position: 1 +description: "Reusable template for creating category index pages" +--- + +# Product or Category Name + +[![Status](https://img.shields.io/badge/Status-Draft-yellow?style=flat-square&logo=git)](./) +[![Category](https://img.shields.io/badge/Category-Product_Index-blue?style=flat-square&logo=readme)](./) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +{/* +BADGE SELECTION GUIDE: +1. Status Badge (Required): Stable, Draft, Review, Deprecated +2. Category Badge (Required): Product, Standard, Guide +*/} + +> **CATEGORY**: Product Landing Page | Standards Index +> **PURPOSE**: Central navigation hub for [Topic] +> **OWNER**: Team Name / Owner Email + +--- + +## Overview + +[Provide a high-level summary of this product, sections, or category. Explain the value proposition and what users can expect to find in the sub-pages.] + +## Key Resources + +[Optional: Highlight the most important or frequently used pages within this section.] + +```markdown +* [**Getting Started**](./getting-started): Quick onboarding guide. +* [**Architecture**](./design/architecture): High-level design. +* [**Operations**](./operations): Daily runbooks. +``` + +## Contents + +[This section automatically lists all pages located within this folder. Ensure every new page added to this folder has proper front matter.] + +import DocCardList from '@theme/DocCardList'; + + + +--- + +## Support & Contact + +[How to get help for this product/category] + +* **Slack**: `#channel-name` +* **Issues**: [Link to Issue Tracker] +* **Maintainers**: [List maintainers] diff --git a/standards/documentation/templates/golden-template.mdx b/standards/documentation/templates/golden-template.mdx new file mode 100644 index 000000000..2ef1d6ad2 --- /dev/null +++ b/standards/documentation/templates/golden-template.mdx @@ -0,0 +1,80 @@ +--- +title: "Golden Document Template" +sidebar_label: "Document Template" +sidebar_position: 2 +description: "Reusable template for creating new standard documents" +--- + +# Document Title + +[![Status](https://img.shields.io/badge/Status-Draft-yellow?style=flat-square&logo=git)](./) +[![Category](https://img.shields.io/badge/Category-Runbook-blue?style=flat-square&logo=readme)](./) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +{/* +BADGE SELECTION GUIDE: +1. Status Badge (Required): Stable, Draft, Review, Deprecated +2. Category Badge (Required): Runbook, Design, Reference, Standard +3. Role Badge (Optional): Architect, Admin, Operator +*/} + +> **DOCUMENT CATEGORY**: Runbook | Design | Reference +> **SCOPE**: Define the scope of this document +> **PURPOSE**: Define the purpose of this document +> **AUDIENCE**: Target audience (e.g., Implementation Engineers) +> **EST. TIME**: 30 Minutes + +--- + +## Overview + +[Brief introduction to the topic. Explain **what** this is and **why** it matters.] + +## Prerequisites + +Before beginning, ensure the following requirements are met: + +* [ ] Prerequisite 1 +* [ ] Prerequisite 2 + +## Procedure + +### Task 1: Action Name + +[Description of the step] + +:::info +Use callouts for important information that isn't a warning. +::: + +```powershell +# Example Powershell Code Block +$Variable = "Value" +Write-Host "Executing step..." +``` + +### Task 2: Next Action + +[Description of step 2] + +:::warning +Use warning callouts for critical information or potential risks. +::: + +## Validation + +Verify the success of the procedure: + +1. Method 1 +2. Method 2 + +## Troubleshooting + +| Issue | Cause | Resolution | +|-------|-------|------------| +| Error message | Likely cause | Fix steps | + +## Related Documentation + +* [Link to related doc 1](#) +* [Link to related doc 2](#) diff --git a/standards/documentation/templates/index.mdx b/standards/documentation/templates/index.mdx new file mode 100644 index 000000000..13fe139f6 --- /dev/null +++ b/standards/documentation/templates/index.mdx @@ -0,0 +1,48 @@ +--- +title: Document Templates +sidebar_label: Templates +sidebar_position: 99 +description: "Reusable document templates for creating standardized documentation" +--- + +# Document Templates + +[![Templates](https://img.shields.io/badge/Type-Templates-purple?style=flat-square)](../documentation-standards.mdx) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +> **DOCUMENT CATEGORY**: Templates +> **SCOPE**: Reusable templates for documentation creation +> **PURPOSE**: Provide copy-paste ready templates that follow documentation standards +> **MASTER REFERENCE**: [Documentation Standards](../documentation-standards.mdx) + +**Status**: Active +**Applies To**: All TierPoint Product Technology documentation +**Last Updated**: 2026-01-30 + +--- + +## Overview + +This folder contains reusable templates for creating documentation that follows the [Documentation Standards](../documentation-standards.mdx). Copy these templates when creating new documents. + +## Available Templates + +import DocCardList from '@theme/DocCardList'; + + + +## How to Use + +1. **Choose the appropriate template** based on your document type +2. **Copy the entire file** to your target location +3. **Replace all placeholder text** (text in `[brackets]` or `{braces}`) +4. **Update the frontmatter** with actual values +5. **Remove instruction comments** before publishing + +## Template Types + +| Template | Use For | +|----------|---------| +| [Index Template](./golden-index-template.mdx) | Category landing pages, product index pages | +| [Document Template](./golden-template.mdx) | Standard documents, runbooks, guides | + diff --git a/standards/docusaurus/docusaurus-features.mdx b/standards/docusaurus/docusaurus-features.mdx new file mode 100644 index 000000000..f361db59f --- /dev/null +++ b/standards/docusaurus/docusaurus-features.mdx @@ -0,0 +1,131 @@ +--- +title: Docusaurus Features & Standards +description: Reference guide for Docusaurus-specific features, components, and configuration used in TierPoint documentation. +sidebar_label: Docusaurus Features +sidebar_position: 95 +--- + +# Docusaurus Features & Standards + +[![Reference](https://img.shields.io/badge/Type-Reference-purple?style=flat-square)](../documentation/documentation-standards.mdx) +[![Docusaurus](https://img.shields.io/badge/Platform-Docusaurus-blue?style=flat-square&logo=docusaurus)](https://docusaurus.io) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +> **DOCUMENT CATEGORY**: Reference +> **SCOPE**: Docusaurus-specific features, components, and configuration +> **PURPOSE**: Guide for using Docusaurus features in TierPoint documentation +> **MASTER REFERENCE**: [Documentation Standards](../documentation/documentation-standards.mdx) + +**Status**: Active +**Applies To**: TierPoint Product Technology Documentation Site +**Last Updated**: 2026-01-30 + +--- + +This guide outlines the specific Docusaurus features, components, and configurations standardized for this documentation repository. It explains how to use dynamic components and control navigation behavior. + +## Navigation & Sidebar Control + +### Handling Index Pages (Double-Menu Issue) + +To prevent index pages from appearing twice in the sidebar (once as the category dropdown and again as a child item), we use a specific pattern: + +1. **Sidebar Configuration**: The Category is explicitly linked to the index document in `sidebars.js`. +2. **Hidden Sidebar Item**: The index document itself is visually hidden from the child list using a custom front matter property. + +**Required Front Matter for Index Pages:** + +```yaml +--- +sidebar_class_name: hidden-sidebar-item +--- +``` + +### Sidebar Positioning + +Control the order of items in the autogenerated sidebar using `sidebar_position`. + +* **Index Files**: Typically don't need a position if they are the Category link. +* **Standard Docs**: Number them sequentially if strict ordering is required. +* **Default**: Alphabetical by filename if no position is provided. + +```yaml +--- +sidebar_position: 1 # Appears at the top +--- +``` + +## Dynamic Components + +### DocCardList + +The `` component automatically generates a grid of cards for all documents contained within the current category. This is the **standard** for "Documentation Structure" sections in index pages. + +**Usage:** + +```jsx +import DocCardList from '@theme/DocCardList'; + +## Documentation Structure + + +``` + +**Parameters & Customization:** + +* **Default Behavior**: Displays direct children of the current category. +* **FILTERING**: Currently, we rely on the folder structure. To exclude an item from this list, you can set `sidebar_class_name: hidden-sidebar-item` in that item's front matter, though this also hides it from the sidebar. +* **Explicit Items**: You can technically pass an array of items to `items={...}`, but our standard is to rely on auto-generation for maintenance ease. + +### Admonitions (Callouts) + +Use these standard blocks to highlight critical information. + +**Syntax:** + +```markdown +:::note title +Content here +::: +``` + +**Available Types:** + +* `:::note` - General neutral information. +* `:::tip` - Best practices, shortcuts, or helpful advice. +* `:::info` - Important context that isn't a warning. +* `:::warning` - Potential issues or validation requirements. +* `:::danger` - Destructive actions or service-impacting risks. + +### Tabs + +Use Tabs when providing multiple methods for a single task (e.g., Portal vs. PowerShell). + +**Usage:** + +```jsx +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + This is an apple 🍎 + + + This is an orange 🍊 + + +``` + +## Front Matter Reference + +Standard properties used in our Markdown files: + +| Property | Description | values | +| :--- | :--- | :--- | +| `id` | Unique ID for the document. Mandatory for Index pages to ensure stable linking. | `standards/index` | +| `title` | The H1 title displayed on the page. | String | +| `sidebar_label` | Shorter text for the sidebar menu if the Title is too long. | String | +| `sidebar_class_name` | CSS class for sidebar styling. | `hidden-sidebar-item` | +| `description` | Meta description for SEO and search summaries. | String | +| `draft` | If `true`, the page is only visible in development. | `true` / `false` | diff --git a/standards/docusaurus/index.mdx b/standards/docusaurus/index.mdx new file mode 100644 index 000000000..f6ae298af --- /dev/null +++ b/standards/docusaurus/index.mdx @@ -0,0 +1,83 @@ +--- +title: Docusaurus Standards +sidebar_label: Docusaurus +sidebar_position: 6 +description: "Docusaurus site features, configuration, and best practices" +--- + +# Docusaurus Standards + +[![Docusaurus](https://img.shields.io/badge/Type-Docusaurus-purple?style=flat-square&logo=docusaurus)](https://docusaurus.io) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +> **DOCUMENT CATEGORY**: Standards Index +> **SCOPE**: Docusaurus documentation platform configuration and features +> **PURPOSE**: Guide for using Docusaurus features in TierPoint documentation +> **MASTER REFERENCE**: [Documentation Standards](../documentation/documentation-standards.mdx) + +**Status**: Active +**Applies To**: TierPoint Product Technology Documentation Site +**Last Updated**: 2026-01-30 + +--- + +Standards and guides for working with the Docusaurus documentation site platform. + +## Overview + +This section covers: + +- **Site features** - Tabs, callouts, code blocks, diagrams +- **Configuration** - Site settings and customization +- **Best practices** - Performance, accessibility, SEO + +## Standards in This Section + +import DocCardList from '@theme/DocCardList'; + + + +## Quick Reference + +| Standard | Purpose | +|----------|---------| +| [Docusaurus Features](./docusaurus-features.mdx) | Available features and components | + +## Key Features + +### Tabs Component + +```mdx +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + Content + Content + +``` + +### Callout Blocks + +```mdx +:::info +Informational callout +::: + +:::warning +Warning callout +::: + +:::tip +Helpful tip +::: +``` + +### Mermaid Diagrams + +````mdx +```mermaid +flowchart LR + A --> B --> C +``` +```` diff --git a/standards/fictional-company-policy.mdx b/standards/fictional-company-policy.mdx new file mode 100644 index 000000000..517685c18 --- /dev/null +++ b/standards/fictional-company-policy.mdx @@ -0,0 +1,137 @@ +--- +title: Fictional Company Policy +sidebar_label: Fictional Company Policy +sidebar_position: 2 +description: "Mandatory use of Infinite Improbability Corp (IIC) as the fictional company across all AzureLocal repositories." +--- + +# Fictional Company Policy + +[![Standard](https://img.shields.io/badge/Type-Standard-green?style=flat-square)](./index.mdx) +[![AzureLocal](https://img.shields.io/badge/AzureLocal-Community-0078D4?style=flat-square)](https://azurelocal.cloud) + +> **DOCUMENT CATEGORY**: Standard +> **SCOPE**: All AzureLocal repositories +> **PURPOSE**: Standardize fictional company references across all examples, configs, and documentation + +**Status**: Active +**Last Updated**: 2026-03-17 + +--- + +## Policy + +All AzureLocal repositories use **one** fictional company for examples, sample configurations, walkthroughs, and documentation: **Infinite Improbability Corp (IIC)**. + +:::warning Mandatory +Never use `contoso`, `fabrikam`, `adventure-works`, `woodgrove`, or other Microsoft example names. +**IIC only** — in every repo, every example, every sample config. +::: + +--- + +## Fictional Identity Reference Card + +| Attribute | Value | +|-----------|-------| +| **Full Name** | Infinite Improbability Corp | +| **Abbreviation** | IIC | +| **Domain (public)** | `improbability.cloud` / `iic.cloud` | +| **Domain (on-prem AD)** | `iic.local` | +| **NetBIOS Name** | `IMPROBABLE` | +| **Entra ID Tenant** | `improbability.onmicrosoft.com` | +| **Email Pattern** | `user@improbability.cloud` | +| **Origin** | A nod to *The Hitchhiker's Guide to the Galaxy* | + +--- + +## Naming Patterns + +### Cluster & Node Names + +| Resource | Pattern | Example | +|----------|---------|---------| +| Cluster | `iic-clus` | `iic-clus01` | +| Node | `iic--n` | `iic-01-n01`, `iic-01-n02` | +| iDRAC/BMC | `iic--bmc` | `iic-01-bmc01` | + +### Azure Resources + +| Resource | Pattern | Example | +|----------|---------|---------| +| Resource Group | `rg-iic--<##>` | `rg-iic-sofs-01` | +| Key Vault | `kv-iic-` | `kv-iic-platform` | +| Storage Account | `stiic<##>` | `stiicwitness01` | +| Virtual Network | `vnet-iic--<##>` | `vnet-iic-mgmt-01` | +| Log Analytics | `law-iic--<##>` | `law-iic-monitor-01` | + +### Active Directory + +| Resource | Pattern | Example | +|----------|---------|---------| +| OU path | `OU=,DC=iic,DC=local` | `OU=Servers,DC=iic,DC=local` | +| Service account | `svc.iic.` | `svc.iic.deploy` | +| Admin account | `admin@iic.local` | — | +| Group | `grp-iic-` | `grp-iic-sofs-admins` | + +### IP Addresses (examples) + +| Network | Range | Example | +|---------|-------|---------| +| Management | `10.0.0.0/24` | `10.0.0.11` – `10.0.0.14` | +| Storage | `10.0.1.0/24` | `10.0.1.11` – `10.0.1.14` | +| Compute | `10.0.2.0/24` | `10.0.2.0/24` | + +--- + +## Real Identities + +These are the **real** identities used for authorship and attribution — not fictional: + +| Name | Usage | +|------|-------| +| **Azure Local Cloud** | The community project and GitHub org. Used in repo URLs, `azurelocal.cloud` site references, and org-level documentation. | +| **Hybrid Cloud Solutions** | Author/maintainer LLC. Used in script headers (`Author: Hybrid Cloud Solutions`), copyright notices, and contact references. | + +--- + +## Usage Examples + +### In `variables.example.yml` + +```yaml +azure: + subscription_id: "00000000-0000-0000-0000-000000000000" + tenant_id: "00000000-0000-0000-0000-000000000000" + resource_group: "rg-iic-sofs-01" + location: "eastus" + +keyvault: + name: "kv-iic-platform" + +azure_local: + cluster_name: "iic-clus01" + cluster_domain: "iic.local" +``` + +### In Documentation + +> Infinite Improbability Corp's production SOFS cluster (`iic-clus01`) serves +> FSLogix profile containers for 500 AVD session hosts across two Azure Local nodes. + +### In Scripts + +```powershell +# Example: Connect to IIC cluster +$clusterName = "iic-clus01" +$domain = "iic.local" +$credential = Get-Secret -Vault "kv-iic-platform" -Name "svc-iic-deploy" +``` + +--- + +## Enforcement + +- **PR review**: Reviewers should flag any use of `contoso`, `fabrikam`, or other Microsoft example names +- **CI**: Vale linting rules can flag non-IIC fictional company names (when configured) +- **Config validation**: `variables.example.yml` files should use IIC naming patterns in all placeholder values diff --git a/standards/index.mdx b/standards/index.mdx new file mode 100644 index 000000000..5386bd7f8 --- /dev/null +++ b/standards/index.mdx @@ -0,0 +1,49 @@ +--- +title: Standards & Frameworks +description: Organization-wide standards, guidelines, and frameworks for all AzureLocal repositories. +sidebar_class_name: hidden-sidebar-item +--- + +# Standards & Frameworks + +[![Standards](https://img.shields.io/badge/Type-Standards-purple?style=flat-square)](./documentation/documentation-standards.mdx) +[![AzureLocal](https://img.shields.io/badge/AzureLocal-Community-0078D4?style=flat-square)](https://azurelocal.cloud) + +> **DOCUMENT CATEGORY**: Standards Index +> **SCOPE**: All AzureLocal repositories +> **PURPOSE**: Central navigation hub for all standards, guidelines, and frameworks +> **MASTER REFERENCE**: [Documentation Standards](./documentation/documentation-standards.mdx) + +**Status**: Active +**Applies To**: All AzureLocal repositories +**Last Updated**: 2026-03-17 + +--- + +:::info Authoritative Source +This `standards/` section is the **single source of truth** for org-wide conventions. +Individual repos should **not** duplicate these pages — link here instead. +See [Repository Structure](./repo-structure.mdx) for the required file layout every repo must follow. +::: + +This section contains the standards, guidelines, and frameworks used across all AzureLocal repositories. + +## Categories + +| Category | Description | +|----------|-------------| +| [Repository Structure](./repo-structure.mdx) | Required files, directory layout, labels, branch naming | +| [Documentation](./documentation/) | Writing standards, formatting, naming conventions, badges, templates | +| [Scripting](./scripting/) | PowerShell standards, frameworks, organization patterns | +| [Infrastructure](./infrastructure/) | IaC standards, state management, deployment processes | +| [Variable Management](./variable-management/) | Variable governance, schemas, validation, workflows | +| [Solutions](./solutions/) | Solution packaging, multi-tool parity, parallelism, deployment standards | +| [Provisioning](./provisioning/) | Step-by-step runbook structure, deployment options, stage/step patterns | +| [Fictional Company Policy](./fictional-company-policy.mdx) | Infinite Improbability Corp (IIC) usage guidelines | +| [Docusaurus](./docusaurus/) | Site features, components, configuration | + +## Browse All Standards + +import DocCardList from '@theme/DocCardList'; + + diff --git a/standards/infrastructure/index.mdx b/standards/infrastructure/index.mdx new file mode 100644 index 000000000..b47a177a4 --- /dev/null +++ b/standards/infrastructure/index.mdx @@ -0,0 +1,64 @@ +--- +title: Infrastructure Standards +sidebar_label: Infrastructure +sidebar_position: 4 +description: "Infrastructure as Code, state management, and deployment process standards" +--- + +# Infrastructure Standards + +[![Infrastructure](https://img.shields.io/badge/Type-Infrastructure-purple?style=flat-square&logo=terraform)](./infrastructure-generation-deployment-process.mdx) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +> **DOCUMENT CATEGORY**: Standards Index +> **SCOPE**: Infrastructure as Code, state management, and deployment workflows +> **PURPOSE**: Define IaC standards and deployment processes for Azure Local environments +> **MASTER REFERENCE**: [Documentation Standards](../documentation/documentation-standards.mdx) + +**Status**: Active +**Applies To**: All Azure Local environment repositories +**Last Updated**: 2026-01-30 + +--- + +Standards for Infrastructure as Code (IaC), Terraform state management, and deployment processes. + +## Overview + +This section covers: + +- **Deployment processes** - Infrastructure generation and deployment workflows +- **State management** - Terraform state file handling and governance +- **IaC patterns** - Reusable infrastructure patterns + +## Standards in This Section + +import DocCardList from '@theme/DocCardList'; + + + +## Quick Reference + +| Standard | Purpose | +|----------|---------| +| [Infrastructure Generation & Deployment](./infrastructure-generation-deployment-process.mdx) | End-to-end deployment workflow | +| [State Management](./state-management.mdx) | Terraform state governance | + +## Key Concepts + +### Infrastructure Pipeline + +```mermaid +flowchart LR + A[Generate Variables] --> B[Validate Config] + B --> C[Plan Infrastructure] + C --> D[Review Changes] + D --> E[Apply Changes] + E --> F[Update State] +``` + +### State File Governance + +- Remote state storage (Azure Storage Account) +- State locking during operations +- State file backup and recovery diff --git a/standards/infrastructure/infrastructure-generation-deployment-process.mdx b/standards/infrastructure/infrastructure-generation-deployment-process.mdx new file mode 100644 index 000000000..995b408d2 --- /dev/null +++ b/standards/infrastructure/infrastructure-generation-deployment-process.mdx @@ -0,0 +1,578 @@ +--- +id: infrastructure-generation-deployment-process +title: Infrastructure Generation Deployment Process +sidebar_label: Infrastructure Generation Deployment Process +--- + +# Infrastructure Generation and Deployment Process + +[![Standards](https://img.shields.io/badge/Type-Standard-purple?style=flat-square)](./) +[![Infrastructure](https://img.shields.io/badge/Category-Infrastructure-blue?style=flat-square&logo=terraform)](./) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +> **DOCUMENT CATEGORY**: Standard +> **SCOPE**: End-to-end infrastructure generation and deployment workflow +> **PURPOSE**: Define the process for generating and deploying infrastructure using environment-config.yml +> **MASTER REFERENCE**: [Variable Management](../variable-management/) + +**Status**: Active +**Applies To**: All Azure Local environment repositories +**Last Updated**: 2026-01-30 + +--- + +## Overview + +This document outlines the complete process for: + +1. Configuring your environment using `environment-config.yml` +2. Generating `infrastructure.yml` from templates and variables +3. Discovering existing resources (Azure tenant and on-premises hardware) +4. Completing and validating the infrastructure file +5. Marking deployment readiness + +**Related Documentation:** + +- Variable Registry Process - Complete variable registry workflows *(to be created)* +- Infrastructure Generation Flags - Scenario-based filtering logic *(to be created)* +- Automation Tools Reference - Tool usage guide *(to be created)* + +--- + +## Prerequisites + +- Environment repository cloned (e.g., `-azl-env-`) +- Master Workplace Project repository available (for `master-registry.yaml`) +- Azure CLI and PowerShell installed +- Access to target Azure tenant (for discovery) +- Access to iDRAC/iLO interfaces (for hardware discovery, if applicable) + +--- + +## Process Steps + +### Task 1: Configure Environment Parameters + +**File:** `environment-config.yml` (create in your environment repo root) + +**Action:** Define WHAT infrastructure to generate: + +- Node count and storage topology +- Services to deploy (AVD, Arc, Azure VMs) +- IP addressing scheme +- Network structure + +**Templates Available:** + +- `template/configs/environment-config-simple.yml` - Quick start (2-node minimal) +- `template/configs/environment-config-REFERENCE.yml` - Complete options reference +- `template/configs/examples/*.yml` - Real-world scenarios + +**Example:** + +```powershell +# Copy a template that matches your scenario +Copy-Item "C:\git\master-workplace-project\template\configs\examples\2node-switchless.yml" ` + "C:\git\\environment-config.yml" + +# Edit key values: +# - environment.name +# - starting_ip +# - storage_adapter_ip_start +# - address_space +code environment-config.yml +``` + +**Result:** `environment-config.yml` defines generation parameters + +**Reference:** Environment Config Templates *(to be created)* + +--- + +### Task 2: Verify Master Registry is Current + +**File:** `master-registry.yaml` (in your environment repo) + +**Action:** Ensure you have the latest variable definitions and templates. + +**Methods:** + +**Manual Sync:** + +```powershell +# Copy latest master-registry.yaml from master repo +Copy-Item "C:\git\master-workplace-project\variables\master-registry.yaml" ` + "C:\git\\variables\master-registry.yaml" +``` + +**Automated Sync (Future):** + +```powershell +# Will be automated via sync script +.\scripts\Sync-MasterRegistry.ps1 +``` + +**Verification:** + +```powershell +# Check file date and size +Get-Item ".\variables\master-registry.yaml" | Select-Object LastWriteTime, Length +``` + +**Result:** Local `master-registry.yaml` matches master source + +**Reference:** Variable Registry Process - Sync Workflows *(to be created)* + +--- + +### Task 3: Generate infrastructure.yml + +**Script:** `Generate-Infrastructure.ps1` + +**Action:** Generate `infrastructure.yml` structure from templates and variables. + +**Usage:** + +```powershell +# Navigate to your environment repo +cd C:\git\ + +# Generate infrastructure.yml with appropriate flags +.\scripts\Generate-Infrastructure.ps1 ` + -ConfigPath "environment-config.yml" ` + -azure_local ` + -avd_azure_local ` + -WhatIf + +# Review WhatIf output, then run actual generation +.\scripts\Generate-Infrastructure.ps1 ` + -ConfigPath "environment-config.yml" ` + -azure_local ` + -avd_azure_local +``` + +**Infrastructure Flags:** + +- `-azure_local` - Azure Local cluster nodes +- `-avd_azure` - AVD in Azure cloud +- `-avd_azure_local` - AVD on Azure Local +- `-arc_scvmm` - Arc Resource Bridge for SCVMM +- `-arc_hybrid_vms` - Arc-enabled VMs +- `-wsfc_s2d` - Windows Failover Cluster with S2D +- `-wsfc_san` - Windows Failover Cluster with SAN + +**Result:** `infrastructure.yml` created with: + +- Proper node array structure (based on `node_count`) +- Network intents (based on `storage_topology`) +- Logical networks +- Service definitions (AVD, Arc, etc.) +- **Empty/placeholder hardware sections** (discovery will populate) + +**Reference:** Infrastructure Generation Flags *(to be created)* + +--- + +### Task 4: Verify infrastructure.yml Base Values + +**File:** `infrastructure.yml` (newly generated) + +**Action:** Verify generation was successful and base values populated correctly. + +**Checks:** + +```powershell +# 1. Verify file was created +Test-Path ".\infrastructure.yml" + +# 2. Validate YAML syntax +.\scripts\Validate-Infrastructure.ps1 -ConfigPath "infrastructure.yml" + +# 3. Check node count matches config +$config = Get-Content "environment-config.yml" -Raw | ConvertFrom-Yaml +$infra = Get-Content "infrastructure.yml" -Raw | ConvertFrom-Yaml +$infra.compute.cluster_nodes.Count -eq $config.compute.azure_local.cluster_node_count + +# 4. Verify key sections exist (v4.0.0 schema) +$infra.compute.cluster_nodes # Should have N nodes +$infra.networking.onprem.network_intents # Should match storage topology +$infra.networking.onprem.logical_networks # Should have VLAN definitions +``` + +**What to Verify:** + +- Node count correct +- IP addresses match pattern from `environment-config.yml` +- Storage topology correct (switchless vs switched) +- Service sections present (AVD, Arc, etc.) +- Hardware sections empty (expected - discovery will fill) + +**Result:** `infrastructure.yml` structure validated, ready for discovery + +--- + +### Task 5: Run Azure Tenant Discovery + +**Script:** `Inventory-AzureTenant.ps1` (template available) + +**Location:** `template/scripts/discovery/Inventory-AzureTenant.ps1` + +**Action:** Discover existing Azure resources in target tenant. + +**Usage:** + +```powershell +# Authenticate to Azure +Connect-AzAccount -Tenant "00000000-aaaa-bbbb-cccc-111111111111" + +# Run discovery (reads tenant from infrastructure.yml or prompts) +.\scripts\discovery\Inventory-AzureTenant.ps1 + +# Or specify tenant explicitly +.\scripts\discovery\Inventory-AzureTenant.ps1 ` + -TenantId "00000000-aaaa-bbbb-cccc-111111111111" + +# Include RBAC and Entra ID (comprehensive) +.\scripts\discovery\Inventory-AzureTenant.ps1 -IncludeRBAC -IncludeEntraId + +# Review discovered resources +code discovery/azure-inventory.json +``` + +**What Gets Discovered:** + +- Management groups and subscriptions +- Resource groups and all resources +- Azure Local (Stack HCI) clusters and resources +- Networking (VNets, NSGs, subnets, NICs, public IPs) +- Compute (VMs, disks, availability sets, images) +- Storage accounts and file shares +- Security (Key Vaults, managed identities) +- Monitoring (Log Analytics, Application Insights) +- Policy assignments +- RBAC role assignments (optional) +- Entra ID objects (optional) + +**Output:** `discovery/azure-inventory.json` + +**Decision Point:** +Review `azure-inventory.json` and decide: + +- Which resources to **import** into `infrastructure.yml` (existing resources to manage) +- Which resources to **skip** (will be created new) +- Which resources to **ignore** (CI/CD runners, test resources) + +**Import Process:** + +```powershell +# Preview what will be imported (safe WhatIf) +.\scripts\discovery\Update-InfrastructureFromDiscovery.ps1 -Azure -WhatIf + +# Import Azure resources +.\scripts\discovery\Update-InfrastructureFromDiscovery.ps1 -Azure + +# Import with new resource addition disabled (update only) +.\scripts\discovery\Update-InfrastructureFromDiscovery.ps1 -Azure -AddNew:$false +``` + +**Result:** `infrastructure.yml` populated with existing Azure resources + +**Reference:** + +- Template Discovery Scripts *(to be created)* +- Tenant Discovery Workflows *(to be created)* + +--- + +### Task 6: Run On-Premises Hardware Discovery + +**Scripts:** + +- `Get-DellServerInventory-FromiDRAC.ps1` (Dell hardware via Redfish API) +- `Inventory-OnPrem.ps1` (network devices) + +**Location:** `template/scripts/discovery/` + +**Action:** Discover actual hardware details from iDRAC interfaces and network devices. + +**Usage:** + +**Dell Server Discovery (iDRAC Redfish API):** + +```powershell +# Single server (credential prompt) +.\scripts\discovery\Get-DellServerInventory-FromiDRAC.ps1 -iDRACIP "10.10.10.11" + +# Multiple servers with credential +$cred = Get-Credential # iDRAC admin credentials +.\scripts\discovery\Get-DellServerInventory-FromiDRAC.ps1 ` + -iDRACIP @("10.10.10.11", "10.10.10.12") ` + -Credential $cred + +# With post-discovery import automation +.\scripts\discovery\Get-DellServerInventory-FromiDRAC.ps1 ` + -iDRACIP "10.10.10.11" ` + -RunPostDiscovery + +# Review discovered hardware +code discovery/idrac-inventory.json +``` + +**On-Premises Network Devices (Optional):** + +```powershell +# Discover all devices defined in infrastructure.yml +.\scripts\discovery\Inventory-OnPrem.ps1 + +# Filter specific device types +.\scripts\discovery\Inventory-OnPrem.ps1 -DeviceType udm,switch + +# Review discovered devices +code discovery/onprem-inventory.json +``` + +**What Gets Discovered:** + +**Dell Servers (iDRAC):** + +- iDRAC IP addresses and configuration +- Server manufacturer and model (e.g., "PowerEdge R760xa", "AX-7525") +- CPU model, core count, hyperthreading status +- RAM capacity, speed, configuration (per DIMM) +- Disk counts, sizes, types (NVMe, SSD, HDD by controller) +- NIC models, MAC addresses, speeds, firmware +- BIOS and firmware versions +- Thermal sensors and power supply status + +**Network Devices (On-Premises):** + +- Ubiquiti UniFi Dream Machine (UDM) - REST API +- Ubiquiti EdgeRouter - REST/SSH +- OpenGear console servers - REST API +- Dell/Sodola switches - REST/SSH +- Generic SNMP devices + +**Decision Point:** +Review `discovery/idrac-inventory.json` and decide: + +- Which hardware details to **import** (discovered servers match expected nodes) +- Which servers to **exclude** (not part of this deployment) +- Whether discovered hardware meets requirements (CPU, RAM, disk) + +**Import Process:** + +```powershell +# Preview hardware import (safe WhatIf) +.\scripts\discovery\Update-InfrastructureFromDiscovery.ps1 -iDRAC -WhatIf + +# Import hardware details +.\scripts\discovery\Update-InfrastructureFromDiscovery.ps1 -iDRAC + +# Import all discovery sources (Azure + iDRAC + On-Prem) +.\scripts\discovery\Update-InfrastructureFromDiscovery.ps1 -All +``` + +**Result:** `infrastructure.yml` hardware sections populated with actual server details + +**Reference:** + +- Template Discovery Scripts *(to be created)* +- Hardware Discovery Process *(to be created)* +- On-Premises Discovery Process *(to be created)* + +--- + +### Task 7: Complete Manual Values + +**File:** `infrastructure.yml` + +**Action:** Review and populate any remaining fields that require manual input. + +**Interactive Tool (Future):** + +```powershell +# Run interactive completion wizard +.\scripts\Complete-Infrastructure.ps1 -ConfigPath "infrastructure.yml" + +# Wizard will prompt for: +# - Domain administrator credentials → Where to store? (Key Vault secret name) +# - Certificate paths → Which certificate to use for service X? +# - Custom DNS servers → IP addresses? +# - VPN shared keys → Key Vault secret reference? +# - Optional features → Enable feature Y? (yes/no) +``` + +**Manual Review Fields:** +Look for placeholders in `infrastructure.yml`: + +- `{{PLACEHOLDER}}` - Needs manual input +- `{{KEY_VAULT_SECRET:secret-name}}` - Reference to Key Vault secret +- `null` or empty strings in required fields + +**Common Manual Fields:** + +- Domain administrator credentials (Key Vault reference) +- Local administrator passwords (Key Vault reference) +- Certificate thumbprints or paths +- VPN shared keys (Key Vault reference) +- Custom VLAN IDs (if not auto-assigned) +- Optional service toggles (enable/disable features) + +**Validation:** + +```powershell +# Validate all required fields are populated +.\scripts\Validate-Infrastructure.ps1 ` + -ConfigPath "infrastructure.yml" ` + -CheckCompleteness + +# Should return no errors or warnings +``` + +**Result:** `infrastructure.yml` fully populated and ready for deployment + +--- + +### Task 8: Mark Deployment Readiness + +**File:** `infra-state.json` + +**Action:** Update infrastructure state to indicate `infrastructure.yml` is complete and deployment can begin. + +**Update State:** + +```powershell +# Mark infrastructure.yml as complete +.\scripts\Update-InfraState.ps1 ` + -Step "infrastructure_generation" ` + -Status "complete" ` + -Notes "infrastructure.yml validated and ready for deployment" + +# Set next step to deployment +.\scripts\Update-InfraState.ps1 ` + -Step "deployment" ` + -Status "ready" ` + -Notes "Ready to begin deployment process" +``` + +**State File Example:** + +```json +{ + "environment": "my-deployment", + "last_updated": "2025-01-15T10:30:00Z", + "steps": { + "infrastructure_generation": { + "status": "complete", + "completed_at": "2025-01-15T10:30:00Z", + "notes": "infrastructure.yml validated and ready for deployment" + }, + "deployment": { + "status": "ready", + "notes": "Ready to begin deployment process" + } + } +} +``` + +**Result:** Infrastructure generation phase marked complete, deployment phase ready to start + +**Reference:** [State Management](./state-management.mdx) + +--- + +## Process Flow Diagram + +``` +[1. environment-config.yml] + ↓ +[2. Verify master-registry.yaml] + ↓ +[3. Generate-Infrastructure.ps1] → infrastructure.yml (structure only) + ↓ +[4. Verify infrastructure.yml] + ↓ +[5. Discover Azure Tenant] → azure-discovery.json + ↓ +[6. Discover On-Prem Hardware] → hardware-discovery.json + ↓ +[7. Import & Complete infrastructure.yml] → infrastructure.yml (complete) + ↓ +[8. Update infra-state.json] → Deployment Ready +``` + +--- + +## Checklist + +Use this checklist to track your progress: + +- [ ] **Task 1:** Created `environment-config.yml` from template +- [ ] **Task 2:** Verified `master-registry.yaml` is current +- [ ] **Task 3:** Ran `Generate-Infrastructure.ps1` with correct flags +- [ ] **Task 4:** Validated `infrastructure.yml` structure and base values +- [ ] **Task 5:** Ran Azure tenant discovery and imported selected resources +- [ ] **Task 6:** Ran hardware discovery and imported server details +- [ ] **Task 7:** Completed manual fields and validated completeness +- [ ] **Task 8:** Updated `infra-state.json` to mark deployment readiness + +--- + +## Related Documentation + +### Process Documentation + +- Variable Registry Process - Complete variable registry workflows *(to be created)* +- Tenant Discovery Process - Azure resource discovery *(to be created)* +- Hardware Discovery Process - iDRAC/iLO discovery *(to be created)* + +### Standards & References + +- Infrastructure Generation Flags - Scenario filtering *(to be created)* +- Automation Tools Reference - Tool usage guide *(to be created)* +- [State Management](./state-management.mdx) - Infrastructure state tracking + +### Templates & Examples + +- Environment Config Templates - Template documentation *(to be created)* +- Environment Config Examples - Real-world examples *(to be created)* + +--- + +## ❓ Troubleshooting + +### Issue: Generate-Infrastructure.ps1 fails with "Config file not found" + +**Solution:** Ensure `environment-config.yml` is in the current directory or provide full path with `-ConfigPath` + +### Issue: infrastructure.yml has wrong node count + +**Solution:** Verify `node_count` in `environment-config.yml` and regenerate + +### Issue: Hardware discovery can't connect to iDRAC + +**Solution:** + +1. Verify network connectivity: `Test-NetConnection -ComputerName 10.10.10.11 -Port 443` +2. Check iDRAC credentials +3. Verify iDRAC is enabled and accessible + +### Issue: Validation fails on infrastructure.yml + +**Solution:** Run with `-Verbose` to see detailed error messages + +```powershell +.\scripts\Validate-Infrastructure.ps1 -ConfigPath "infrastructure.yml" -Verbose +``` + +--- + +**Version Control** + +- Created: 2025-12-15 by Product Technology Team +- Last Edited: 2025-12-15 by Product Technology Team +- Version: 1.0.0 +- Tags: process, infrastructure-generation, deployment, workflow +- Keywords: environment-config, infrastructure.yml, discovery, deployment +- Author: Product Technology Team + diff --git a/standards/infrastructure/state-management.mdx b/standards/infrastructure/state-management.mdx new file mode 100644 index 000000000..59e7e33ff --- /dev/null +++ b/standards/infrastructure/state-management.mdx @@ -0,0 +1,570 @@ +--- +id: state-management +title: State Management +sidebar_label: State Management +--- + +# Infrastructure Deployment State Management + +[![Standards](https://img.shields.io/badge/Type-Standard-purple?style=flat-square)](./infrastructure-generation-deployment-process.mdx) +[![State](https://img.shields.io/badge/Category-State_Management-blue?style=flat-square&logo=json)](./) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +> **DOCUMENT CATEGORY**: Standard +> **SCOPE**: Infrastructure deployment state tracking using infra-state.json +> **PURPOSE**: Define state management patterns for tracking deployment progress +> **MASTER REFERENCE**: [Infrastructure Generation](./infrastructure-generation-deployment-process.mdx) + +**Status**: Active +**Applies To**: All Azure Local environment repositories +**Last Updated**: 2026-01-30 + +--- + +## Overview + +The `infra-state.json` file tracks the deployment state of an environment through all phases of infrastructure generation, discovery, validation, and deployment. It provides: + +- **Progress tracking** - Know exactly which steps are complete +- **Validation status** - Track validation results and blockers +- **Discovery metadata** - Record when discovery was last run +- **Deployment readiness** - Gate deployment based on completion criteria +- **Audit trail** - Historical record of state changes + +**File Location:** `infra-state.json` (root of environment repository) + +--- + +## State Management Lifecycle + +### Phase 1: Generation + +**Trigger:** After `Generate-Infrastructure.ps1` creates `infrastructure.yml` + +**State:** +```json +{ + "environment_name": "my-deployment", + "infrastructure_version": "1.0.0", + "state": { + "phase": "generation", + "status": "complete", + "timestamp": "2025-12-15T10:00:00Z" + }, + "generation": { + "config_file": "environment-config.yml", + "flags_used": ["azure_local", "avd_azure_local"], + "node_count": 2, + "storage_topology": "switchless", + "generated_at": "2025-12-15T10:00:00Z", + "generated_by": "user@domain.com" + }, + "validation": { + "yaml_syntax": "not_started", + "base_values": "not_started", + "last_validated": null + }, + "discovery": { + "azure_tenant": "not_started", + "hardware_idrac": "not_started", + "onprem_network": "not_started" + }, + "deployment_readiness": { + "is_ready": false, + "blockers": [ + "Azure tenant discovery not run", + "Hardware discovery not run", + "Manual values not completed" + ], + "approved_by": null, + "approved_at": null + } +} +``` + +--- + +### Phase 2: Validation + +**Trigger:** After `Validate-Infrastructure.ps1` checks YAML syntax + +**State Updates:** +```json +{ + "validation": { + "yaml_syntax": "passed", + "base_values": "passed", + "last_validated": "2025-12-15T10:15:00Z", + "errors": [], + "warnings": [ + "Hardware sections empty (expected before discovery)" + ] + } +} +``` + +--- + +### Phase 3: Discovery + +**Trigger:** After `Inventory-AzureTenant.ps1` runs + +**State Updates:** +```json +{ + "state": { + "phase": "discovery", + "status": "in_progress", + "timestamp": "2025-12-15T10:30:00Z" + }, + "discovery": { + "azure_tenant": { + "status": "complete", + "timestamp": "2025-12-15T10:30:00Z", + "resources_found": { + "subscriptions": 1, + "resource_groups": 12, + "vnets": 3, + "vms": 8, + "key_vaults": 2 + }, + "imported": true, + "imported_at": "2025-12-15T10:32:00Z" + }, + "hardware_idrac": "not_started", + "onprem_network": "not_started" + } +} +``` + +**Trigger:** After `Get-DellServerInventory-FromiDRAC.ps1` runs + +**State Updates:** +```json +{ + "discovery": { + "azure_tenant": { + "status": "complete", + "timestamp": "2025-12-15T10:30:00Z" + }, + "hardware_idrac": { + "status": "complete", + "timestamp": "2025-12-15T11:00:00Z", + "servers_found": 2, + "service_tags": ["XXXXXXX", "YYYYYYY"], + "validation": { + "meets_requirements": true, + "issues": [] + }, + "imported": true, + "imported_at": "2025-12-15T11:05:00Z" + }, + "onprem_network": "not_started" + } +} +``` + +--- + +### Phase 4: Completion + +**Trigger:** After manual values completed (interactive wizard or manual editing) + +**State Updates:** +```json +{ + "state": { + "phase": "completion", + "status": "in_progress", + "timestamp": "2025-12-15T11:30:00Z" + }, + "completion": { + "manual_values": { + "status": "complete", + "completed_at": "2025-12-15T11:30:00Z", + "completed_by": "user@domain.com", + "fields_completed": [ + "domain_admin_credential_keyvault_ref", + "local_admin_password_keyvault_ref", + "witness_storage_account_key_keyvault_ref" + ] + }, + "validation_rerun": { + "status": "passed", + "validated_at": "2025-12-15T11:35:00Z", + "errors": [], + "warnings": [] + } + } +} +``` + +--- + +### Phase 5: Deployment Readiness + +**Trigger:** After all gates pass (manual approval) + +**State Updates:** +```json +{ + "state": { + "phase": "ready", + "status": "complete", + "timestamp": "2025-12-15T12:00:00Z" + }, + "deployment_readiness": { + "is_ready": true, + "blockers": [], + "gates_passed": [ + "infrastructure.yml validated", + "Azure tenant discovered and imported", + "Hardware discovered and validated", + "Manual values completed", + "Final validation passed" + ], + "approved_by": "deployment-manager@domain.com", + "approved_at": "2025-12-15T12:00:00Z", + "deployment_scheduled": "2025-12-16T08:00:00Z" + } +} +``` + +--- + +### Phase 6: Deployment + +**Trigger:** During deployment (Terraform/Ansible execution) + +**State Updates:** +```json +{ + "state": { + "phase": "deployment", + "status": "in_progress", + "timestamp": "2025-12-16T08:00:00Z" + }, + "deployment": { + "started_at": "2025-12-16T08:00:00Z", + "started_by": "deploy-service-account@domain.com", + "terraform": { + "init": "complete", + "plan": "complete", + "apply": "in_progress", + "resources_created": 24, + "resources_updated": 3, + "resources_destroyed": 0 + }, + "ansible": { + "playbooks_run": ["foundation.yml"], + "status": "pending" + } + } +} +``` + +--- + +### Phase 7: Post-Deployment + +**Trigger:** After deployment completes successfully + +**Final State:** +```json +{ + "state": { + "phase": "deployed", + "status": "complete", + "timestamp": "2025-12-16T10:30:00Z" + }, + "deployment": { + "started_at": "2025-12-16T08:00:00Z", + "completed_at": "2025-12-16T10:30:00Z", + "duration_minutes": 150, + "terraform": { + "status": "complete", + "resources_created": 42, + "resources_updated": 5, + "resources_destroyed": 0 + }, + "ansible": { + "status": "complete", + "playbooks_run": ["foundation.yml", "security.yml", "monitoring.yml"] + }, + "post_deployment_validation": { + "status": "passed", + "validated_at": "2025-12-16T10:45:00Z", + "checks_passed": [ + "Azure Local cluster online", + "All nodes responding", + "Storage Spaces Direct healthy", + "Network connectivity validated" + ] + } + } +} +``` + +--- + +## State Transitions + +### Valid State Transitions + +```mermaid +graph LR + A[generation] --> B[validation] + B --> C[discovery] + C --> D[completion] + D --> E[ready] + E --> F[deployment] + F --> G[deployed] + G --> H[operational] + + C --> C[discovery loops until complete] + D --> D[completion loops until valid] +``` + +### Phase Definitions + +| Phase | Description | Exit Criteria | +|-------|-------------|---------------| +| **generation** | `infrastructure.yml` created from `environment-config.yml` | File exists and is valid YAML | +| **validation** | YAML syntax and base values validated | No errors, warnings acceptable | +| **discovery** | Azure tenant and hardware discovered | All discovery sources complete and imported | +| **completion** | Manual values filled in | All required fields populated, validation passes | +| **ready** | Deployment approved and scheduled | Approval recorded, blockers cleared | +| **deployment** | Terraform/Ansible execution | Resources created, no errors | +| **deployed** | Infrastructure operational | Post-deployment validation passes | +| **operational** | Normal operations | Ongoing monitoring and maintenance | + +--- + +## Automation Integration + +### Update State from Scripts + +**Example: Update after Azure Discovery** + +```powershell +# Load current state +$state = Get-Content "infra-state.json" | ConvertFrom-Json + +# Update discovery section +$state.discovery.azure_tenant = @{ + status = "complete" + timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") + resources_found = @{ + subscriptions = 1 + resource_groups = 12 + vnets = 3 + vms = 8 + } + imported = $true + imported_at = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") +} + +# Save updated state +$state | ConvertTo-Json -Depth 10 | Set-Content "infra-state.json" +``` + +--- + +### Check Deployment Readiness + +```powershell +# Check if environment is ready for deployment +$state = Get-Content "infra-state.json" | ConvertFrom-Json + +if ($state.deployment_readiness.is_ready -eq $true) { + Write-Host "[SUCCESS] Environment is ready for deployment" -ForegroundColor Green + Write-Host " Approved by: $($state.deployment_readiness.approved_by)" + Write-Host " Approved at: $($state.deployment_readiness.approved_at)" +} else { + Write-Host "[ERROR] Environment is NOT ready for deployment" -ForegroundColor Red + Write-Host "`nBlockers:" -ForegroundColor Yellow + $state.deployment_readiness.blockers | ForEach-Object { + Write-Host " - $_" + } +} +``` + +--- + +### GitLab CI/CD Integration + +```yaml +# .gitlab-ci.yml example +validate-state: + stage: validate + script: + - $state = Get-Content infra-state.json | ConvertFrom-Json + - if ($state.deployment_readiness.is_ready -ne $true) { exit 1 } + only: + - main + +deploy: + stage: deploy + script: + - terraform apply -auto-approve + - .\scripts\Update-InfraState.ps1 -Phase deployment -Status complete + dependencies: + - validate-state + when: manual +``` + +--- + +## State File Schema + +### Complete JSON Schema + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": ["environment_name", "infrastructure_version", "state"], + "properties": { + "environment_name": { + "type": "string", + "description": "Name of the environment" + }, + "infrastructure_version": { + "type": "string", + "description": "Version of infrastructure.yml schema" + }, + "state": { + "type": "object", + "required": ["phase", "status", "timestamp"], + "properties": { + "phase": { + "type": "string", + "enum": ["generation", "validation", "discovery", "completion", "ready", "deployment", "deployed", "operational"] + }, + "status": { + "type": "string", + "enum": ["not_started", "in_progress", "complete", "failed", "blocked"] + }, + "timestamp": { + "type": "string", + "format": "date-time" + } + } + }, + "generation": { "type": "object" }, + "validation": { "type": "object" }, + "discovery": { "type": "object" }, + "completion": { "type": "object" }, + "deployment_readiness": { "type": "object" }, + "deployment": { "type": "object" } + } +} +``` + +--- + +## Troubleshooting + +### Issue: State file out of sync + +**Symptoms:** +- State shows "complete" but steps not actually done +- Timestamps don't match actual work + +**Solution:** +```powershell +# Manually correct state file +$state = Get-Content "infra-state.json" | ConvertFrom-Json + +# Reset to accurate state +$state.state.phase = "discovery" +$state.state.status = "in_progress" +$state.discovery.hardware_idrac = "not_started" + +# Save corrected state +$state | ConvertTo-Json -Depth 10 | Set-Content "infra-state.json" +``` + +--- + +### Issue: Deployment blocked incorrectly + +**Symptoms:** +- All steps complete but `is_ready` still `false` +- Valid blockers list outdated + +**Solution:** +```powershell +# Review blockers +$state = Get-Content "infra-state.json" | ConvertFrom-Json +$state.deployment_readiness.blockers + +# Manually clear if all are resolved +$state.deployment_readiness.blockers = @() +$state.deployment_readiness.is_ready = $true + +# Save updated state +$state | ConvertTo-Json -Depth 10 | Set-Content "infra-state.json" +``` + +--- + +## Best Practices + +### 1. Commit State Changes + +```powershell +# Commit state after major milestones +git add infra-state.json +git commit -m "State: Discovery phase complete (Azure + iDRAC)" +git push +``` + +### 2. Backup Before Deployment + +```powershell +# Create backup before deployment +Copy-Item "infra-state.json" "infra-state-backup-$(Get-Date -Format 'yyyyMMdd-HHmmss').json" +``` + +### 3. Automate State Updates + +Create helper scripts: +```powershell +# Update-InfraState.ps1 +param( + [Parameter(Mandatory)] + [ValidateSet("generation", "validation", "discovery", "completion", "ready", "deployment", "deployed")] + [string]$Phase, + + [Parameter(Mandatory)] + [ValidateSet("not_started", "in_progress", "complete", "failed", "blocked")] + [string]$Status +) + +$state = Get-Content "infra-state.json" | ConvertFrom-Json +$state.state.phase = $Phase +$state.state.status = $Status +$state.state.timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") +$state | ConvertTo-Json -Depth 10 | Set-Content "infra-state.json" +``` + +--- + +## Related Documentation + +- [Infrastructure Generation and Deployment Process](./infrastructure-generation-deployment-process.mdx) - Complete workflow +- Tenant Discovery Process - Azure discovery state tracking *(to be created)* +- Hardware Discovery Process - iDRAC discovery state tracking *(to be created)* +- Variable Registry Process - Variable management *(to be created)* + +--- + +**Version Control** +- Created: 2025-12-15 by Product Technology Team +- Last Edited: 2025-12-15 by Product Technology Team +- Version: 1.0.0 +- Tags: state-management, deployment, tracking +- Keywords: infra-state, deployment-readiness, state-tracking +- Author: Product Technology Team + diff --git a/standards/provisioning/index.mdx b/standards/provisioning/index.mdx new file mode 100644 index 000000000..691317782 --- /dev/null +++ b/standards/provisioning/index.mdx @@ -0,0 +1,83 @@ +--- +title: Provisioning Standards +sidebar_label: Provisioning +sidebar_position: 2 +description: "Standards for step-by-step provisioning runbooks with three deployment options" +--- + +# Provisioning Standards + +[![Provisioning](https://img.shields.io/badge/Type-Provisioning-purple?style=flat-square)](./provisioning-runbook-standard.mdx) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +> **DOCUMENT CATEGORY**: Standards Index +> **SCOPE**: Provisioning runbooks, deployment guides, and implementation documentation +> **PURPOSE**: Define the framework for creating step-by-step provisioning documentation +> **MASTER REFERENCE**: [Documentation Standards](../documentation/documentation-standards.mdx) + +**Status**: Active +**Applies To**: All Azure Local implementation runbooks +**Last Updated**: 2026-01-30 + +--- + +Framework and standards for creating step-by-step provisioning runbooks that guide engineers through deploying and configuring infrastructure. + +## Overview + +This section defines the **3-Option Provisioning Framework** — a structured approach to deployment documentation that supports multiple execution contexts while maintaining consistency. + +### The Three Options + +| Option | Execution Context | Best For | +|--------|-------------------|----------| +| **Option 1: Azure Portal (Manual)** | Web browser, GUI | Learning, single deployments, visual preference | +| **Option 2: Direct Script Execution** | Run ON target node (console/RDP) | Direct console access, interactive sessions | +| **Option 3: Orchestrated Script Execution** | Run FROM management server (remote) | Jump box management, multi-node orchestration | + +> **⚡ Looking for IaC/CI/CD automation?** +> +> Fully automated deployments (Terraform, Bicep, Ansible, CI/CD pipelines) are documented in the product's **Automation** section, not in provisioning runbooks. + +## Standards in This Section + +import DocCardList from '@theme/DocCardList'; + + + +## Quick Reference + +| Standard | Purpose | +|----------|---------| +| [Provisioning Runbook Standard](./provisioning-runbook-standard.mdx) | Core framework, stage/step structure, option definitions | +| [Stage Index Template](./stage-index-template.mdx) | Template for stage overview pages | +| [Step Template](./step-template.mdx) | Template for individual step files | + +## Key Concepts + +### Stage/Step Pattern + +``` +implementation/ +├── stage-01-preparation/ +│ ├── index.mdx # Stage overview (Objective, Tasks, Validation, Outcome) +│ ├── step-01-task.mdx # Step with 3 options +│ └── step-02-task.mdx +├── stage-02-foundation/ +└── stage-03-deployment/ +``` + +### Stage Index Structure + +Each stage index defines: +- **Objective** — WHY this stage exists +- **Tasks** — WHAT gets done (maps to steps) +- **Validation** — HOW to verify stage completion +- **Outcome** — RESULT when stage is complete + +### Step Structure + +Each step provides three execution options: +1. **Option 1: Azure Portal (Manual)** — GUI procedure with validation checklist +2. **Option 2: Direct Script Execution** — Scripts to run ON target node +3. **Option 3: Orchestrated Script Execution** — Scripts to run FROM management server diff --git a/standards/provisioning/provisioning-runbook-standard.mdx b/standards/provisioning/provisioning-runbook-standard.mdx new file mode 100644 index 000000000..ad52cedf0 --- /dev/null +++ b/standards/provisioning/provisioning-runbook-standard.mdx @@ -0,0 +1,565 @@ +--- +id: provisioning-runbook-standard +title: Provisioning Runbook Standard +sidebar_label: Runbook Standard +sidebar_position: 1 +description: "Framework for creating step-by-step provisioning runbooks with the Execution Options pattern" +--- + +# Provisioning Runbook Standard + +[![Standards](https://img.shields.io/badge/Type-Standard-purple?style=flat-square)](../documentation/documentation-standards.mdx) +[![Runbook](https://img.shields.io/badge/Category-Runbook-blue?style=flat-square)](../documentation/documentation-standards.mdx) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +> **DOCUMENT CATEGORY**: Standard +> **SCOPE**: Provisioning and deployment runbook documentation +> **PURPOSE**: Define the Execution Options Framework for step-by-step deployment guides +> **MASTER REFERENCE**: [Documentation Standards](../documentation/documentation-standards.mdx) + +**Status**: Active +**Applies To**: All provisioning, deployment, and implementation runbooks +**Last Updated**: 2026-03-10 + +--- + +## Purpose + +This standard defines the **Execution Options Framework** — a structured approach for creating provisioning runbooks that guide engineers through deploying and configuring infrastructure. + +Documents following this framework must be: + +- **Repeatable**: Any qualified engineer should achieve the same result +- **Context-Aware**: Organized by execution context, not just by tool +- **Validated**: Include verification steps to confirm success +- **Flexible**: Support manual, scripted, orchestrated, and standalone execution + +:::info Relationship to Documentation Standards +This standard extends the [Documentation Standards](../documentation/documentation-standards.mdx) for the specific use case of provisioning runbooks. All general documentation requirements (frontmatter, badges, structure) still apply. +::: + +--- + +## The Execution Options Framework + +### Core Concept + +Provisioning steps are organized by **execution context** (WHERE and HOW you run the commands). Each task presents its execution options using **tabbed navigation** (`` component). + +| Option | Execution Context | Description | +|--------|-------------------|-------------| +| **Manual** | GUI / Physical / Web UI | Human-performed steps — Azure Portal, iDRAC Web UI, on-site inspection, WAC, etc. | +| **Orchestrated Script** | Run FROM management server | Config-driven scripts executed remotely via PSRemoting — reads `infrastructure.yml` | +| **Standalone Script** | Run anywhere (copy-paste) | Self-contained script — all variables declared in `#region CONFIGURATION` at the top before any executable code. No external dependencies. | + +### Task Classification — Select Tabs Before Authoring + +Before writing a task document, **classify its execution target**. The target determines which tabs the document receives. This is not optional — every task must be classified. + +For the full decision flowchart with Mermaid diagram, see the [Tab Selection Decision Logic](./tab-decision-logic.mdx). + +| Execution Target | Tab Profile | Tabs | +|------------------|-------------|------| +| **Azure-Only** (Portal / Azure API operations) | 3 tabs | Azure Portal · Azure CLI / PowerShell · Standalone Script | +| **Windows Server** (On-prem node, GUI/PSRemoting) | Up to 3 tabs | *Contextual GUI* · Orchestrated Script (Mgmt Server) · Standalone Script | +| **Physical / Vendor Hardware** (iDRAC, switch, firewall) | Variable | *Vendor UI* · API/CLI Script (if API exists) · Standalone Script (if scriptable) | +| **Network Device** (FortiGate, Dell switch) | Per-device | Tabs per device model, NOT per execution method (`groupId="device-type"`) | +| **CI/CD Pipeline / Documentation-Only** | No tabs | Single procedure, narrative, validation, or checklist | + +:::danger Classify First, Then Author +**Never start writing tabs without classifying the task.** The classification determines the exact tab count, labels, values, and `groupId`. Skip this step and the document will be wrong. +::: + +### Tab Profiles by Execution Target + +#### Azure-Only Tasks (3 Tabs) + +Azure is a control-plane operation — there is **no node to run on**. + +| Tab | `value` | `label` | Config Source | Default | +|-----|---------|---------|---------------|---------| +| 1 | `manual` | `Azure Portal` | N/A | `default` | +| 2 | `orchestrated` | `Azure CLI / PowerShell` | `infrastructure.yml` | | +| 3 | `standalone` | `Standalone Script` | `#region CONFIGURATION` | | + +```mdx +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + {/* Azure Portal GUI steps */} + + + {/* Config-driven script — reads infrastructure.yml */} + + + {/* Self-contained copy-paste script — #region CONFIGURATION at top */} + + +``` + +#### Windows Server Tasks (Up to 3 Tabs) + +| Tab | `value` | `label` | Config Source | Default | +|-----|---------|---------|---------------|---------| +| 1 | `manual` | *Contextual* (SConfig, ADUC, DNS Manager, WAC, etc.) | N/A | `default` (if present) | +| 2 | `orchestrated` | `Orchestrated Script (Mgmt Server)` | `infrastructure.yml` via PSRemoting | Default if no manual | +| 3 | `standalone` | `Standalone Script` | `#region CONFIGURATION` | | + +```mdx + + + {/* GUI steps */} + + + {/* Config-driven script — reads infrastructure.yml via PSRemoting */} + + + {/* Self-contained copy-paste script — #region CONFIGURATION at top */} + + +``` + +#### Physical / Vendor Hardware Tasks (Variable Tabs) + +| Tab | `value` | `label` | When to Include | +|-----|---------|---------|-----------------| +| 1 (default) | `manual` | *Vendor-specific* (iDRAC Web UI, Switch Console, FortiGate GUI) | Always | +| 2 (if API) | `api` | `API / CLI Script` | Only if vendor provides API/CLI | +| 3 (if scriptable) | `standalone` | `Standalone Script` | Only if the task is scriptable | + +#### Network Device Tasks (Per-Device Tabs) + +Network device tasks use `groupId="device-type"` with tabs **per device model**, not per execution method: + +```mdx + + + {/* FortiGate-specific procedure */} + + + {/* Dell switch-specific procedure */} + + +``` + +#### CI/CD Pipeline and Documentation-Only + +**No tabs.** Write a single narrative procedure, validation checklist, or documentation-only page. + +### `groupId` Values + +| Context | `groupId` | +|---------|-----------| +| Execution option tabs (Azure, Windows, Hardware) | `deployment-method` | +| Network device variant tabs | `device-type` | + +### Default Tab Logic + +| Condition | Default Tab | +|-----------|-------------| +| Manual tab exists | Manual tab is default | +| No manual tab | Orchestrated Script is default | + +Set the default using the `default` attribute on the ``. + +### Tab Labels + +The **Manual** tab label must be contextual to the task: + +| Scenario | Manual Tab Label | +|----------|-----------------| +| Azure Portal action | `Azure Portal` | +| Physical site work | `On-Site Inspection` | +| Hardware management UI | `iDRAC Web UI` | +| Network switch console | `Switch Console` | +| Windows Admin Center | `Windows Admin Center` | +| Windows Server Configuration | `SConfig` | + +Script tab labels are **fixed** — use these exact strings, no variations: + +| Tab | Exact Label | +|-----|-------------| +| Azure CLI / PowerShell (Azure-only tasks) | `Azure CLI / PowerShell` | +| Orchestrated Script (Windows tasks) | `Orchestrated Script (Mgmt Server)` | +| Standalone Script (all task types) | `Standalone Script` | + +### Common Tab Violations + +| Violation | Correction | +|-----------|------------| +| Azure-only task missing `Azure CLI / PowerShell` tab | Add it with `value="orchestrated"` | +| Azure-only task uses label `Orchestrated Script (Mgmt Server)` | Rename to `Azure CLI / PowerShell`. Keep `value="orchestrated"` | +| Windows task uses label `Azure CLI / PowerShell` | Rename to `Orchestrated Script (Mgmt Server)` | +| Standalone Script has variables scattered through script body | Move all variables into `#region CONFIGURATION` at the top | +| Orchestrated Script uses hardcoded values | Replace with `$config.{path}` references from `infrastructure.yml` | +| Inconsistent tab labels across documents | Use exact labels from this document. No abbreviations or rewording | +| Tabs on CI/CD or documentation-only pages | Remove tabs entirely | +| Network device task with execution-method tabs | Use `groupId="device-type"` with per-device tabs instead | + +### When to Use Each Option + +| Option | Best For | Requirements | +|--------|----------|-------------| +| **Manual** | Learning, single deployments, visual preference, physical tasks | Web browser / physical access / vendor UI | +| **Orchestrated Script** | Managing from jump box, Azure API operations, multiple nodes | Management server or workstation, `infrastructure.yml`, helpers | +| **Standalone Script** | Sharing, demos, one-off runs, copy-paste use, environments without toolkit | PowerShell only — no config file, no helpers needed | + +### Script Variants Within Each Option + +For Orchestrated / Azure CLI+PS options, multiple script variants are available: + +| Variant | File Suffix | Description | +|---------|-------------|-------------| +| **PowerShell + Azure PowerShell** | `-az.ps1` | Primary. Uses Az modules (`Connect-AzAccount`, `New-AzResource`) | +| **PowerShell + Azure CLI** | `-azcli.ps1` | PowerShell script using `az` CLI commands | +| **Bash + Azure CLI** | `.sh` | Bash script using `az` CLI commands | + +> **Note**: PowerShell with Azure PowerShell modules is the primary documented language. Alternative implementations follow the same logic. + +### IaC/CI/CD Automation + +Fully automated deployments (Terraform, Bicep, Ansible, CI/CD pipelines) are **NOT** part of provisioning runbooks. They are documented separately in the product's **Automation** section. + +--- + +## Document Organization + +### Stage and Step Structure + +Provisioning documentation follows a hierarchical **stage → step** pattern: + +``` +product/implementation/ +├── stage-01-preparation/ +│ ├── _category_.json # Stage metadata +│ ├── index.mdx # Stage overview (Objective, Tasks, Validation, Outcome) +│ ├── task-01-first-task.mdx # Step with execution options tabs +│ ├── task-02-second-task.mdx +│ └── task-03-third-task.mdx +├── stage-02-foundation/ +│ ├── _category_.json +│ ├── index.mdx +│ └── ... +└── stage-03-deployment/ + └── ... +``` + +### Naming Conventions + +| Type | Pattern | Example | +|------|---------|---------| +| Stage folder | `stage-{nn}-{description}` | `stage-03-azure-foundation` | +| Step file | `task-{nn}-{action-description}.mdx` | `task-01-create-key-vault.mdx` | +| Index file | `index.mdx` | `index.mdx` | +| Category file | `_category_.json` | `_category_.json` | + +:::warning Zero-Padding Required +Always use two-digit numbers with zero-padding (`01`, `02`, `03`) for proper sorting. +::: + +### Script Naming Conventions + +Script naming follows the [PowerShell Organization Standard](../scripting/powershell-organization-standard.mdx) and [Scripting Standards](../scripting/scripting-standards.mdx): + +| Context | Naming Pattern | Example | +|---------|----------------|--------| +| **Orchestrated** | `Invoke-{Resource}.ps1` | `Invoke-KeyVault.ps1` | +| **Standalone** | `{Verb}-{Resource}-Standalone.ps1` | `New-AzKeyVault-Standalone.ps1` | +| **Validation** | `Test-{Component}.ps1` | `Test-KeyVault.ps1` | +| **Configuration** | `Configure-{Service}.ps1` | `Configure-KeyVaultAccess.ps1` | + +--- + +## Stage Index Structure + +Every stage must have an `index.mdx` with these sections: + +### Required Sections + +| Section | Purpose | Content | +|---------|---------|---------| +| **Table of Contents** | Navigation | Links to all steps in this stage | +| **Objective** | WHY | Single sentence — the goal of this stage | +| **Tasks** | WHAT | Bullet list of high-level tasks (maps to steps) | +| **Validation** | HOW to verify | Bullet checklist of success criteria for entire stage | +| **Outcome** | RESULT | Single sentence — state when stage is complete | +| **Navigation** | Wayfinding | Previous/Up/Next links | + +### Example Stage Index + +```mdx +--- +title: "Stage 5 - Platform Key Vault" +sidebar_label: "Stage 5 - Key Vault" +sidebar_position: 5 +description: "Establish centralized secret management for Azure Local deployment" +--- + +# Stage 5 — Platform Key Vault + +## Table of Contents + +- [Task 1 – Create Platform Key Vault](./task-01-create-key-vault.mdx) +- [Task 2 – Configure Access Policies](./task-02-configure-access.mdx) +- [Task 3 – Store Deployment Secrets](./task-03-store-secrets.mdx) + +--- + +## Stage Details + +### Objective + +Establish a platform Key Vault for centralized secret management during Azure Local deployment operations. + +### Tasks + +- Create platform Azure Key Vault (or configure existing) +- Configure RBAC access policies for deployment operators and service principals +- Store deployment secrets (local admin credentials, service principal secrets) + +### Validation + +- [ ] Platform Key Vault accessible and configured +- [ ] RBAC access policies correctly assigned +- [ ] Deployment secrets stored and retrievable by authorized identities + +### Outcome + +Secure, centralized secret management infrastructure ready to support Azure Local deployment operations. + +--- + +## Navigation + +| Previous | Up | Next | +|----------|-----|------| +| [Phase 4 - Identity](../stage-04-identity/) | [Implementation Index](../index.mdx) | [Phase 6 - Management Infrastructure](../stage-06-management/) | +``` + +--- + +## Step Document Structure + +### Required Sections + +Every step document MUST include an **Execution Options** section containing a `` block. Only include tabs for execution methods that genuinely apply to the task. + +| Section | Purpose | +|---------|---------| +| **Overview** | Brief description of what this step accomplishes | +| **Execution Options** | `` block with applicable execution methods | +| **End of Step** | Clear delineation marker | +| **Navigation** | Previous/Up/Next links | + +### Tab Content Requirements + +Each tab should contain the following sub-sections as applicable: + +| Sub-section | Manual | Orchestrated Script | Standalone Script | +|-------------|--------|---------------------|-------------------| +| **When to use** | Yes | Yes | Yes | +| **Procedure / Code** | Step-by-step GUI | Script path + code | Inline code block | +| **Alternative Scripts** | N/A | PowerShell, CLI, Bash | N/A | +| **`#region CONFIGURATION`** | N/A | N/A | Yes (at top, before any executable code) | +| **Validation** | Checklist | Script reference | Inline verification | +| **Links** | Microsoft docs | N/A | N/A | + +--- + +## Orchestrated vs Standalone: Key Differences + +| Aspect | Orchestrated Script | Standalone Script | +|--------|---------------------|-------------------| +| **Naming prefix** | `Invoke-` | `{Verb}-{Noun}-Standalone` | +| **Execution location** | FROM management server | Anywhere | +| **Config source** | `infrastructure.yml` + helpers | `#region CONFIGURATION` block (inline) — **all vars at top before any executable code** | +| **External dependencies** | Config-loader, helpers, PSRemoting | **None** — fully self-contained | +| **Parameters** | Full: `$ConfigPath`, `$Credential`, `$TargetNode`, `$WhatIf`, `$LogPath` | None — all values hardcoded in CONFIGURATION region | +| **Credential resolution** | 1. `-Credential` param → 2. Key Vault → 3. `Get-Credential` prompt | N/A | +| **Remoting** | Uses `Invoke-Command`, PSRemoting | Not required | +| **Pipeline support** | Returns objects for chaining | Basic output | +| **Best for** | Multi-node orchestration, Azure API operations, automated pipelines | Sharing, demos, one-off runs, copy-paste use | + +### Standalone Script Rule + +Every Standalone Script **must** declare all variables in a `#region CONFIGURATION` / `#endregion CONFIGURATION` block **before any executable code**. No variable may be defined outside this block or interleaved with logic. + +```powershell +# CORRECT — all variables at top, then execution logic below +#region CONFIGURATION +$ClusterName = "iic-clus01" # infrastructure.yml: compute.azure_local.cluster_name +$ResourceGroup = "rg-azurelocal-prod" # infrastructure.yml: azure_platform.resource_groups.azl.name +$Location = "eastus2" # infrastructure.yml: azure_platform.region +#endregion CONFIGURATION + +# Execution begins here — never define variables below this line +New-AzStackHCICluster -Name $ClusterName -ResourceGroupName $ResourceGroup -Location $Location +``` + +### Orchestrated Script Required Parameter Contract + +Every `Invoke-` script **must** expose these parameters: + +| Parameter | Type | Default | Purpose | +|-----------|------|---------|---------| +| `-ConfigPath` | `[string]` | `""` | Path to `infrastructure.yml`. Auto-discovered if not provided. | +| `-Credential` | `[PSCredential]` | `$null` | Override credential resolution. | +| `-TargetNode` | `[string[]]` | `@()` (all nodes) | Limit to named nodes. Empty = run all. | +| `-WhatIf` | `[switch]` | `$false` | Dry-run mode. | +| `-LogPath` | `[string]` | `""` (auto) | Override log file path. | + +Plus `[CmdletBinding()]` for built-in `-Verbose` and `-Debug`. + +**Credential resolution order** (no exceptions): +1. `-Credential` parameter (if passed, use immediately) +2. Key Vault — `Az.KeyVault` module first, `az` CLI fallback +3. `Get-Credential` interactive prompt (pre-filled username) + +### Example: Orchestrated vs Standalone + +**Orchestrated Script** — Run FROM management server: +```powershell +# Invoke-ConfigureNTP.ps1 +[CmdletBinding()] +param( + [string]$ConfigPath = "", + [PSCredential]$Credential, + [string[]]$TargetNode = @(), + [switch]$WhatIf, + [string]$LogPath = "", + [string[]]$NtpServers = @() # YAML-overridable: override compute.azure_local.ntp_servers +) +$cfg = Get-Content (Resolve-ConfigPath $ConfigPath) | ConvertFrom-Yaml +$servers = if ($NtpServers.Count) { $NtpServers } else { $cfg.compute.azure_local.ntp_servers } +# ... credential resolution, logging, PSRemoting execution +``` + +**Standalone Script** — Copy-paste ready, no dependencies: +```powershell +# Configure-NTP-Standalone.ps1 +#region CONFIGURATION +$NtpServers = @("ntp1.iic.local", "ntp2.iic.local") # NTP server addresses +$ClusterNodes = @("iic-01-n01", "iic-01-n02") # Target node names +$LogFile = "C:\Logs\ntp-config.log" # Log output path +#endregion CONFIGURATION + +foreach ($node in $ClusterNodes) { + Invoke-Command -ComputerName $node -ScriptBlock { + param($Servers) + w32tm /config /manualpeerlist:($Servers -join ' ') /syncfromflags:manual /reliable:YES /update + } -ArgumentList (, $NtpServers) +} +``` + +--- + +## Validation Standards + +### Validation Should Be a Mix + +Each step's validation should include: + +1. **Manual checklists** — Human-verifiable items (`[ ]` checkboxes) +2. **Verification commands** — Inline commands to run +3. **Validation script references** — Path to reusable validation scripts + +### Example Validation Section + +````mdx +### Validation + +- [ ] Key Vault created successfully +- [ ] Key Vault accessible from deployment workstation +- [ ] Permission model set to RBAC + +```powershell +# Verify Key Vault creation +Get-AzKeyVault -VaultName $KeyVaultName | Format-List VaultName, Location, EnableRbacAuthorization +``` + +**Validation Script**: `scripts/validation/key-vault/Test-KeyVault.ps1` +```` + +--- + +## Quality Checklist + +Before publishing a provisioning document, verify: + +### Classification +- [ ] **Execution target classified** (Azure-Only / Windows Server / Physical Hardware / Network Device / CI-CD / Docs-Only) +- [ ] Tab count and labels match the profile for the classified target +- [ ] Correct `groupId` used (`deployment-method` or `device-type`) + +### Structure +- [ ] Frontmatter includes all required fields +- [ ] Execution Options uses `` with correct `groupId` +- [ ] Only tabs for genuinely applicable methods are included +- [ ] Step numbering is sequential and zero-padded +- [ ] Navigation links are accurate + +### Stage Index +- [ ] Table of Contents links to all steps +- [ ] Objective is a single clear sentence +- [ ] Tasks map to steps (same count) +- [ ] Validation is a checkable list +- [ ] Outcome describes end state + +### Step Content +- [ ] Overview clearly explains the purpose +- [ ] Each tab has "When to use" guidance +- [ ] Manual tab only exists when the action is genuinely possible via GUI +- [ ] Script tabs include primary script + alternatives table +- [ ] Standalone tab has `#region CONFIGURATION` / `#endregion CONFIGURATION` at top — **all variables before any executable code** +- [ ] Validation includes checklists, commands, and script references + +### Code Quality +- [ ] All code blocks specify language +- [ ] No emojis in any script block +- [ ] Variables use consistent naming +- [ ] Comments explain non-obvious steps +- [ ] Commands are copy-paste ready +- [ ] Orchestrated scripts reference `infrastructure.yml` paths with inline comments (`# path.to.value`) +- [ ] Standalone scripts have no external dependencies +- [ ] All Standalone variables are in `#region CONFIGURATION` — none defined in the script body + +### Script Naming +- [ ] Orchestrated scripts use `Invoke-` prefix +- [ ] Standalone scripts use `-Standalone.ps1` suffix +- [ ] Orchestrated scripts expose the full required parameter contract +- [ ] Orchestrated scripts return objects for pipeline +- [ ] Standalone scripts have `#region CONFIGURATION` / `#endregion CONFIGURATION` + +--- + +## Related Standards + +- **[Documentation Standards](../documentation/documentation-standards.mdx)** — Base documentation requirements +- **[Badge Library](../documentation/badge-library.mdx)** — Badge definitions and usage +- **[Naming Conventions](../documentation/naming-conventions.mdx)** — Resource naming patterns +- **[PowerShell Organization Standard](../scripting/powershell-organization-standard.mdx)** — Script standards +- **[Scripting Standards](../scripting/scripting-standards.mdx)** — Script options, naming, and the Standalone pattern +- **[Scripting Framework](../scripting/scripting-framework.mdx)** — Script architecture and `infrastructure.yml` +- **[Tab Selection Decision Logic](./tab-decision-logic.mdx)** — Flowchart and profiles for selecting the correct tab structure per task + +--- + +## Templates + +Use these templates for new documents: + +- **[Stage Index Template](./stage-index-template.mdx)** — Template for stage overview pages +- **[Step Template](./step-template.mdx)** — Template for individual step files (with ``) + +--- + +**Version Control** + +- Created: 2026-01-30 by Product Technology Team +- Last Updated: 2026-03-10 by Product Technology Team +- Version: 5.0.0 +- Tags: standards, provisioning, runbook, deployment, execution-options-framework, tabs, task-classification +- Keywords: provisioning, runbook, step-by-step, deployment, azure, powershell, orchestrated, standalone, tabs, classification, tab-profiles +- Author: Product Technology Team diff --git a/standards/provisioning/stage-index-template.mdx b/standards/provisioning/stage-index-template.mdx new file mode 100644 index 000000000..f099ab0a7 --- /dev/null +++ b/standards/provisioning/stage-index-template.mdx @@ -0,0 +1,183 @@ +--- +title: Stage Index Template +sidebar_label: Stage Index Template +sidebar_position: 2 +description: "Copy-paste template for creating stage overview pages" +--- + +# Stage Index Template + +[![Template](https://img.shields.io/badge/Type-Template-purple?style=flat-square)](./provisioning-runbook-standard.mdx) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +> **DOCUMENT CATEGORY**: Template +> **SCOPE**: Stage overview pages for provisioning runbooks +> **PURPOSE**: Provide a copy-paste ready template for stage index files +> **MASTER REFERENCE**: [Provisioning Runbook Standard](./provisioning-runbook-standard.mdx) + +**Status**: Active +**Last Updated**: 2026-01-30 + +--- + +## Overview + +This template provides the structure for stage index files (`index.mdx`) in provisioning runbooks. Each stage index should be **lean and high-level** — a roadmap to the steps, not implementation details. + +### Stage Index Structure + +| Section | Purpose | Content | +|---------|---------|---------| +| **Table of Contents** | Navigation | Links to all steps in this stage | +| **Objective** | WHY | Single sentence — the goal of this stage | +| **Tasks** | WHAT | Bullet list of high-level tasks (maps to steps) | +| **Validation** | HOW to verify | Bullet checklist of success criteria | +| **Outcome** | RESULT | Single sentence — state when stage is complete | +| **Navigation** | Wayfinding | Previous/Up/Next links | + +:::tip How to Use This Template +1. Copy the **Template** section below +2. Paste into your new `index.mdx` file +3. Replace all `{PLACEHOLDER}` values +4. Ensure Tasks bullet count matches Step count +::: + +--- + +## Template + +Copy everything below this line: + +--- + +````mdx +--- +title: "Stage N - {Stage Title}" +sidebar_label: "Stage N - {Short Label}" +sidebar_position: {n} +description: "{Brief description of what this stage accomplishes}" +--- + +# Stage N — {Stage Title} + +## Table of Contents + +- [Task 1 – {First Task}](./task-01-{first-task}.mdx) +- [Task 2 – {Second Task}](./task-02-{second-task}.mdx) +- [Task 3 – {Third Task}](./task-03-{third-task}.mdx) + +--- + +## Stage Details + +### Objective + +{Single sentence describing WHY this stage exists and its goal.} + +### Tasks + +- {High-level task 1 — maps to Step 1} +- {High-level task 2 — maps to Step 2} +- {High-level task 3 — maps to Step 3} + +### Validation + +- [ ] {Success criterion 1} +- [ ] {Success criterion 2} +- [ ] {Success criterion 3} + +### Outcome + +{Single sentence describing the RESULT when this stage is complete.} + +--- + +## Navigation + +| Previous | Up | Next | +|----------|-----|------| +| [Stage {N-1} - {Previous Stage}](../stage-{nn-1}-{previous-stage}/) | [Implementation Index](../index.mdx) | [Stage {N+1} - {Next Stage}](../stage-{nn+1}-{next-stage}/) | +```` + +--- + +## Example: Completed Stage Index + +```mdx +--- +title: "Stage 5 - Platform Key Vault" +sidebar_label: "Stage 5 - Key Vault" +sidebar_position: 5 +description: "Establish centralized secret management for Azure Local deployment" +--- + +# Stage 5 — Platform Key Vault + +## Table of Contents + +- [Task 1 – Create Platform Key Vault](./task-01-create-key-vault.mdx) +- [Task 2 – Configure Access Policies](./task-02-configure-access.mdx) +- [Task 3 – Store Deployment Secrets](./task-03-store-secrets.mdx) + +--- + +## Stage Details + +### Objective + +Establish a platform Key Vault for centralized secret management during Azure Local deployment operations. + +### Tasks + +- Create platform Azure Key Vault (or configure existing) +- Configure RBAC access policies for deployment operators and service principals +- Store deployment secrets (local admin credentials, service principal secrets) + +### Validation + +- [ ] Platform Key Vault accessible and configured +- [ ] RBAC access policies correctly assigned +- [ ] Deployment secrets stored and retrievable by authorized identities + +### Outcome + +Secure, centralized secret management infrastructure ready to support Azure Local deployment operations. + +--- + +## Navigation + +| Previous | Up | Next | +|----------|-----|------| +| [Phase 4 - Identity](../stage-04-identity/) | [Implementation Index](../index.mdx) | [Phase 6 - Management Infrastructure](../stage-06-management/) | +``` + +--- + +## Checklist + +Before publishing a stage index: + +- [ ] Title uses format: `Stage N - {Title}` +- [ ] Table of Contents links to all steps +- [ ] Objective is a single clear sentence +- [ ] Tasks count matches Steps count +- [ ] Each Task maps to a Step +- [ ] Validation items are checkable (`[ ]`) +- [ ] Outcome describes the end state +- [ ] Navigation links are correct + +--- + +## Related Templates + +- **[Step Template](./step-template.mdx)** — Template for individual step files +- **[Provisioning Runbook Standard](./provisioning-runbook-standard.mdx)** — Framework definition + +--- + +**Version Control** + +- Created: 2026-01-30 by Product Technology Team +- Last Updated: 2026-01-30 by Product Technology Team +- Version: 2.0.0 diff --git a/standards/provisioning/step-template.mdx b/standards/provisioning/step-template.mdx new file mode 100644 index 000000000..5a18d6d69 --- /dev/null +++ b/standards/provisioning/step-template.mdx @@ -0,0 +1,694 @@ +--- +title: Step Template +sidebar_label: Step Template +sidebar_position: 3 +description: "Copy-paste template for creating provisioning step files with tabbed execution options" +--- + +# Step Template + +[![Template](https://img.shields.io/badge/Type-Template-purple?style=flat-square)](./provisioning-runbook-standard.mdx) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +> **DOCUMENT CATEGORY**: Template +> **SCOPE**: Individual step files for provisioning runbooks +> **PURPOSE**: Provide a copy-paste ready template for step files with tabbed execution options +> **MASTER REFERENCE**: [Provisioning Runbook Standard](./provisioning-runbook-standard.mdx) + +**Status**: Active +**Last Updated**: 2026-03-10 + +--- + +## Overview + +This template provides the structure for step files in provisioning runbooks. Each step uses **tabbed execution options** so readers can switch between methods without scrolling. + +### Step 1: Classify the Task + +Before copying a template, **classify the execution target** to select the correct variant: + +| Execution Target | Template to Use | Tab Count | +|------------------|----------------|-----------| +| **Azure-Only** (Portal / Azure API operations) | [Azure-Only Template](#azure-only-template-3-tabs) | 3 | +| **Windows Server** (On-prem node, GUI/PSRemoting) | [Windows Server Template](#windows-server-template-3-tabs) | Up to 3 | +| **Physical / Vendor Hardware** | Adapt Azure-Only template — use vendor-specific labels | Variable | +| **Network Device** | Use `groupId="device-type"` — tabs per device model | Variable | +| **CI/CD / Documentation-Only** | No tabs — single narrative procedure | 0 | + +See the [Tab Selection Decision Logic](./tab-decision-logic.mdx) for the full decision flowchart and the [Provisioning Runbook Standard](./provisioning-runbook-standard.mdx) for complete tab profile definitions. + +:::tip How to Use This Template +1. **Classify** the execution target (Azure? Windows? Hardware? Network? Docs-only?) +2. Copy the matching **template variant** below +3. Paste into your new `task-NN-{action}.mdx` file +4. Replace all `{PLACEHOLDER}` values +5. **Remove tabs that do not apply** — only keep execution methods that genuinely work for this task +6. If no manual/portal option exists, move `default` to the Orchestrated tab +7. Fill in the procedure details for each remaining tab +::: + +--- + +## Azure-Only Template (3 Tabs) + +Use this template for tasks that operate against **Azure control-plane APIs** — creating resources, configuring Azure services, querying Azure Resource Manager. There is **no target node**. + +Copy everything below this line: + +--- + +````mdx +--- +title: "Task N: {Action Description}" +sidebar_label: "Task N: {Short Label}" +sidebar_position: {n} +description: "{Brief description of what this step accomplishes}" +--- + +# Task N: {Action Description} + +{Brief 1-2 sentence description of what this step accomplishes. Include any skip conditions.} + +--- + +## Execution Options + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + +### Azure Portal + +> **When to use**: Learning Azure Local, single deployment, prefer visual interface + +#### Procedure + +1. **Navigate to {Service}**: + - In Azure Portal, search for **{Service Name}** + - Click **+ Create** + +2. **Configure {Resource}**: + | Field | Value | Source | + |-------|-------|--------| + | Subscription | `` | `infrastructure.yml: azure.subscription.id` | + | Resource group | `{rg-name}` | `infrastructure.yml: {path.to.resource_group}` | + | Name | `{resource-name}` | `infrastructure.yml: {path.to.name}` | + | Region | `{region}` | `infrastructure.yml: azure.location` | + +3. **Complete creation**: + - Click **Review + create** + - Verify settings are correct + - Click **Create** + +#### Validation + +- [ ] {Resource} created successfully +- [ ] {Resource} accessible from deployment workstation + +#### Links + +- [Microsoft Docs: {Topic}](https://learn.microsoft.com/en-us/azure/{path}/) + + + + +### Azure CLI / PowerShell + +> **When to use**: Scripted Azure operations from management workstation or pipeline — config-driven via `infrastructure.yml` + +#### Script + +**Primary**: `scripts/deploy/{stage}/{phase}/{task}/powershell/Invoke-{Resource}.ps1` + +**Alternatives**: +| Variant | Path | +|---------|------| +| Azure CLI | `scripts/deploy/{stage}/{phase}/{task}/azure-cli/Invoke-{Resource}.ps1` | +| Bash | `scripts/deploy/{stage}/{phase}/{task}/bash/invoke-{resource}.sh` | + +#### Code + +```powershell +# ============================================================================ +# Script: Invoke-{Resource}.ps1 +# Execution: Run from management workstation — reads infrastructure.yml +# Prerequisites: Azure PowerShell modules, authenticated to Azure +# ============================================================================ + +#Requires -Modules Az.{Module}, Az.Resources + +[CmdletBinding()] +param( + [string]$ConfigPath = "", + [PSCredential]$Credential, + [string[]]$TargetNode = @(), + [switch]$WhatIf, + [string]$LogPath = "" +) + +# Load configuration +$cfg = Get-Content (Resolve-ConfigPath $ConfigPath) | ConvertFrom-Yaml + +# Extract values from configuration — verify paths in infrastructure.schema.json +$ResourceName = $cfg.{path}.{to}.name # {path}.{to}.name +$ResourceGroup = $cfg.{path}.{to}.resource_group # {path}.{to}.resource_group +$Location = $cfg.azure_platform.region # azure_platform.region + +# Create resource +Write-Host "Creating {Resource}: $ResourceName" + +$resource = New-Az{Resource} ` + -Name $ResourceName ` + -ResourceGroupName $ResourceGroup ` + -Location $Location + +Write-Host "{Resource} created: $($resource.Id)" + +# Return object for pipeline +$resource +``` + +#### Validation + +```powershell +Get-Az{Resource} -Name $ResourceName -ResourceGroupName $ResourceGroup | Format-List Name, Location, {Property} +``` + +**Validation Script**: `scripts/validation/{category}/Test-{Component}.ps1` + + + + +### Standalone Script + +> **When to use**: Copy-paste ready — no config file, no helpers, no dependencies. Ideal for sharing, demos, or environments without the toolkit. + +#### Code + +```powershell +# ============================================================================ +# Script: {Verb}-{Resource}-Standalone.ps1 +# Execution: Run anywhere — fully self-contained, no external dependencies +# Prerequisites: Azure PowerShell modules, authenticated to Azure +# ============================================================================ + +#Requires -Modules Az.{Module}, Az.Resources + +#region CONFIGURATION +# ── Edit these values to match your environment ────────────────────────────── +$ResourceName = "{resource-name}" # Resource name +$ResourceGroup = "{rg-name}" # Target resource group +$Location = "{region}" # Azure region (e.g., eastus2) +#endregion CONFIGURATION + +# Create resource +Write-Host "Creating {Resource}: $ResourceName" + +New-Az{Resource} ` + -Name $ResourceName ` + -ResourceGroupName $ResourceGroup ` + -Location $Location + +Write-Host "{Resource} '$ResourceName' created successfully" + +# Verify +Get-Az{Resource} -Name $ResourceName -ResourceGroupName $ResourceGroup | Format-List Name, Location, {Property} +``` + +:::info No External Dependencies +This script is completely self-contained. All values are defined in the `#region CONFIGURATION` block at the top. Edit those values and run — no `infrastructure.yml`, no config-loader, no helpers required. +::: + + + + +--- + +## Navigation + +| Previous | Up | Next | +|----------|-----|------| +| [Task {N-1} – {Previous Action}](./task-{nn-1}-{previous-action}.mdx) | [Stage Index](./index.mdx) | [Task {N+1} – {Next Action}](./task-{nn+1}-{next-action}.mdx) | +```` + +--- + +## Windows Server Template (3 Tabs) + +Use this template for tasks that operate **on Windows Server nodes** — installing features, configuring OS settings, managing services. This profile applies to all Windows tasks regardless of whether the node has the toolkit installed. The Manual tab label must be context-specific (SConfig, ADUC, DNS Manager, WAC, etc.). + +Copy everything below this line: + +--- + +````mdx +--- +title: "Task N: {Action Description}" +sidebar_label: "Task N: {Short Label}" +sidebar_position: {n} +description: "{Brief description of what this step accomplishes}" +--- + +# Task N: {Action Description} + +{Brief 1-2 sentence description of what this step accomplishes. Include any skip conditions.} + +--- + +## Execution Options + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + +### {Contextual Manual Title} + +> **When to use**: Prefer visual interface, single node, learning the process + +#### Procedure + +1. **{Step 1}**: + - {Instructions} + +2. **{Step 2}**: + - {Instructions} + +#### Validation + +- [ ] {Check 1} +- [ ] {Check 2} + + + + +### Orchestrated Script Execution + +> **When to use**: Managing from jump box or management server — config-driven via `infrastructure.yml` and PSRemoting + +#### Script + +**Primary**: `scripts/deploy/{stage}/{phase}/{task}/powershell/Invoke-{Action}.ps1` + +**Alternatives**: +| Variant | Path | +|---------|------| +| Azure CLI | `scripts/deploy/{stage}/{phase}/{task}/azure-cli/Invoke-{Action}.ps1` | +| Bash | `scripts/deploy/{stage}/{phase}/{task}/bash/invoke-{action}.sh` | + +#### Code + +```powershell +# ============================================================================ +# Script: Invoke-{Action}.ps1 +# Execution: Run FROM management server (remote orchestration via PSRemoting) +# Prerequisites: PSRemoting enabled, infrastructure.yml accessible +# ============================================================================ + +[CmdletBinding()] +param( + [string]$ConfigPath = "", + [PSCredential]$Credential, + [string[]]$TargetNode = @(), + [switch]$WhatIf, + [string]$LogPath = "", + # YAML-overridable parameters + [{type}]${Variable1} = $null # Override {path}: infrastructure.yml: {yaml.path} +) + +# Load configuration +$cfg = Get-Content (Resolve-ConfigPath $ConfigPath) | ConvertFrom-Yaml + +# Extract values — inline comment shows YAML path +$val1 = if (${Variable1}) { ${Variable1} } else { $cfg.{yaml}.{path}.{key1} } # {yaml}.{path}.{key1} +$val2 = $cfg.{yaml}.{path}.{key2} # {yaml}.{path}.{key2} +$nodes = if ($TargetNode.Count) { $TargetNode } else { $cfg.compute.nodes.Keys } + +# Execute remotely +$results = @() +foreach ($node in $nodes) { + Write-Host "[$node] Running {action}..." + + if ($WhatIf) { + Write-Host "[$node] WhatIf: would {action} with val1=$val1" + continue + } + + $result = Invoke-Command -ComputerName $node -Credential $Credential -ScriptBlock { + param($Val1, $Val2) + {Remote execution logic} + } -ArgumentList $val1, $val2 + + $results += [PSCustomObject]@{ + Node = $node + Status = if ($result) { "Success" } else { "Failed" } + Detail = $result + } +} + +$results | Format-Table Node, Status, Detail -AutoSize +# Return for pipeline +$results +``` + +#### Validation + +```powershell +Invoke-Command -ComputerName $nodes -ScriptBlock { + {Verification command} +} | Format-Table PSComputerName, {Property} -AutoSize +``` + +**Validation Script**: `scripts/validation/{category}/Test-{Component}.ps1` + + + + +### Standalone Script + +> **When to use**: Copy-paste ready — no config file, no helpers, no dependencies. Run from anywhere: management server, laptop, or directly on the node. + +#### Code + +```powershell +# ============================================================================ +# Script: {Verb}-{Resource}-Standalone.ps1 +# Execution: Run anywhere — fully self-contained, no external dependencies +# Prerequisites: {Required modules or features} +# ============================================================================ + +#region CONFIGURATION +# ── Edit these values to match your environment ────────────────────────────── +${Variable1} = "{value1}" # {Description} — e.g., iic-01-n01 +${Variable2} = "{value2}" # {Description} +$ClusterNodes = @("{node1}", "{node2}") # Target node hostnames +#endregion CONFIGURATION + +# Execute +Write-Host "Configuring {Resource}..." + +foreach ($node in $ClusterNodes) { + Invoke-Command -ComputerName $node -ScriptBlock { + param($Var1, $Var2) + {Remote execution logic} + } -ArgumentList ${Variable1}, ${Variable2} +} + +Write-Host "{Resource} configured successfully" + +# Verify +foreach ($node in $ClusterNodes) { + Invoke-Command -ComputerName $node -ScriptBlock { + {Verification command} + } +} +``` + +:::info No External Dependencies +All values are defined in the `#region CONFIGURATION` block at the top of the script. Edit those values and run — no `infrastructure.yml`, no config-loader, no helpers required. +::: + + + + +--- + +## Navigation + +| Previous | Up | Next | +|----------|-----|------| +| [Task {N-1} – {Previous Action}](./task-{nn-1}-{previous-action}.mdx) | [Stage Index](./index.mdx) | [Task {N+1} – {Next Action}](./task-{nn+1}-{next-action}.mdx) | +```` + +--- + +## Example: Completed Step File (Azure-Only Task) + +````mdx +--- +title: "Task 1: Create Platform Key Vault" +sidebar_label: "Task 1: Create Key Vault" +sidebar_position: 1 +description: "Create a platform Azure Key Vault for centralized deployment secret management" +--- + +# Task 1: Create Platform Key Vault + +Create a platform Azure Key Vault for centralized deployment secret management. Skip this step if using an existing Key Vault — proceed to Task 2 to verify permissions. + +:::info Task Classification +**Execution Target**: Azure-Only (control-plane API operation) +**Tab Profile**: 3 tabs — Azure Portal · Azure CLI / PowerShell · Standalone Script +::: + +--- + +## Execution Options + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + +### Azure Portal + +> **When to use**: Learning Azure Local, single deployment, prefer visual interface + +#### Procedure + +1. **Navigate to Key Vaults**: + - In Azure Portal, search for **Key vaults** + - Click **+ Create** + +2. **Configure Key Vault**: + | Field | Value | Source | + |-------|-------|--------| + | Subscription | `` | `infrastructure.yml: azure.subscription.id` | + | Resource group | `rg-management-prod` | `infrastructure.yml: key_vaults.platform.resource_group` | + | Key vault name | `kv-platform-prod-eus2` | `infrastructure.yml: key_vaults.platform.name` | + | Region | `East US 2` | `infrastructure.yml: azure.location` | + | Pricing tier | Standard | — | + +3. **Access Configuration**: + - Permission model: **Azure role-based access control** (recommended) + - Click **Next: Access configuration** + +4. **Networking**: + - Configure as per security requirements + - Click **Review + create** → **Create** + +#### Validation + +- [ ] Key Vault created successfully +- [ ] Key Vault accessible from deployment workstation +- [ ] Permission model set to RBAC + +#### Links + +- [Microsoft Docs: Create Key Vault](https://learn.microsoft.com/en-us/azure/key-vault/general/quick-create-portal) + + + + +### Azure CLI / PowerShell + +> **When to use**: Scripted Azure operations from management workstation or pipeline — config-driven via `infrastructure.yml` + +#### Script + +**Primary**: `scripts/deploy/02-azure-foundation/phase-02-platform/task-01-key-vault/powershell/Invoke-KeyVault.ps1` + +**Alternatives**: +| Variant | Path | +|---------|------| +| Azure CLI | `scripts/deploy/02-azure-foundation/phase-02-platform/task-01-key-vault/azure-cli/Invoke-KeyVault.ps1` | +| Bash | `scripts/deploy/02-azure-foundation/phase-02-platform/task-01-key-vault/bash/invoke-key-vault.sh` | + +#### Code + +```powershell +# ============================================================================ +# Script: Invoke-KeyVault.ps1 +# Execution: Run from management workstation — reads infrastructure.yml +# Prerequisites: Azure PowerShell modules, authenticated to Azure +# ============================================================================ + +#Requires -Modules Az.KeyVault, Az.Resources + +[CmdletBinding()] +param( + [string]$ConfigPath = "", + [PSCredential]$Credential, + [string[]]$TargetNode = @(), + [switch]$WhatIf, + [string]$LogPath = "" +) + +# Load configuration +$cfg = Get-Content (Resolve-ConfigPath $ConfigPath) | ConvertFrom-Yaml + +# Extract values +$KeyVaultName = $cfg.security.keyvault.kv_name # security.keyvault.kv_name +$ResourceGroup = $cfg.azure_platform.resource_groups.platform.name # azure_platform.resource_groups.platform.name +$Location = $cfg.azure_platform.region # azure_platform.region + +if ($WhatIf) { + Write-Host "WhatIf: would create Key Vault '$KeyVaultName' in '$ResourceGroup' ($Location)" + return +} + +# Create Key Vault +Write-Host "Creating Key Vault: $KeyVaultName" + +$keyVault = New-AzKeyVault ` + -Name $KeyVaultName ` + -ResourceGroupName $ResourceGroup ` + -Location $Location ` + -EnableRbacAuthorization + +Write-Host "Key Vault created: $($keyVault.VaultUri)" + +# Return object for pipeline +$keyVault +``` + +#### Validation + +```powershell +Get-AzKeyVault -VaultName $KeyVaultName | Format-List VaultName, Location, EnableRbacAuthorization +``` + +**Validation Script**: `scripts/validation/key-vault/Test-KeyVault.ps1` + + + + +### Standalone Script + +> **When to use**: Copy-paste ready — no config file, no helpers, no dependencies. + +#### Code + +```powershell +# ============================================================================ +# Script: New-AzKeyVault-Standalone.ps1 +# Execution: Run anywhere — fully self-contained +# Prerequisites: Azure PowerShell modules, authenticated to Azure +# ============================================================================ + +#Requires -Modules Az.KeyVault, Az.Resources + +#region CONFIGURATION +# ── Edit these values to match your environment ────────────────────────────── +$KeyVaultName = "kv-platform-prod-eus2" # Key vault name +$ResourceGroup = "rg-management-prod" # Target resource group +$Location = "eastus2" # Azure region +#endregion CONFIGURATION + +# Create Key Vault +Write-Host "Creating Key Vault: $KeyVaultName" + +New-AzKeyVault ` + -Name $KeyVaultName ` + -ResourceGroupName $ResourceGroup ` + -Location $Location ` + -EnableRbacAuthorization + +Write-Host "Key Vault '$KeyVaultName' created successfully" + +# Verify +Get-AzKeyVault -VaultName $KeyVaultName | Format-List VaultName, Location, EnableRbacAuthorization +``` + +:::info No External Dependencies +Edit the values in the `#region CONFIGURATION` block and run. No `infrastructure.yml`, no config-loader, no helpers required. +::: + + + + +--- + +## Navigation + +| Previous | Up | Next | +|----------|-----|------| +| [Phase 5 Index](./index.mdx) | [Phase 5 Index](./index.mdx) | [Task 2 – Configure Access](./task-02-configure-access.mdx) | +```` + +--- + +## Script Naming Reference + +| Context | Prefix / Suffix | Example | +|---------|-----------------|---------| +| **Orchestrated** | `Invoke-` | `Invoke-KeyVault.ps1` | +| **Standalone** | `-Standalone.ps1` | `New-AzKeyVault-Standalone.ps1` | +| **Validation** | `Test-` | `Test-KeyVault.ps1` | + +--- + +## Checklist + +Before publishing a step file: + +### Classification +- [ ] **Execution target classified** (Azure-Only / Windows Server / Physical Hardware / Network Device / CI-CD / Docs-Only) +- [ ] Correct template variant used (Azure-Only 3-tab / Windows 3-tab / other) +- [ ] Tab count and labels match the profile for the classified target +- [ ] Correct `groupId` used (`deployment-method` or `device-type`) + +### Structure +- [ ] Title uses format: `Task N: {Action}` +- [ ] Uses `` (or `groupId="device-type"` for network) +- [ ] Only tabs for genuinely applicable methods are included +- [ ] Each tab has "When to use" guidance +- [ ] Navigation links are correct + +### Manual Tab (if applicable) +- [ ] Step-by-step procedure with numbered steps +- [ ] Configuration table with Source column +- [ ] Validation checklist +- [ ] Links to Microsoft docs +- [ ] **Action is genuinely possible via this interface** (no fabricated steps) + +### Azure CLI / PowerShell or Orchestrated Script Tab +- [ ] Primary script path provided +- [ ] Alternatives table with all variants +- [ ] Code block with parameterized script +- [ ] Script uses `Invoke-` prefix +- [ ] Script exposes full required parameter contract (`$ConfigPath`, `$Credential`, `$TargetNode`, `$WhatIf`, `$LogPath`) +- [ ] Script reads `infrastructure.yml` via config-loader — no hardcoded values +- [ ] `[CmdletBinding()]` present +- [ ] Script returns object for pipeline +- [ ] Correct label: `Azure CLI / PowerShell` for Azure tasks, `Orchestrated Script (Mgmt Server)` for Windows +- [ ] Validation command and script reference included + +### Standalone Script Tab +- [ ] Script name uses `-Standalone.ps1` suffix +- [ ] `#region CONFIGURATION` / `#endregion CONFIGURATION` block at top — **before any executable code** +- [ ] **All variables declared inside the CONFIGURATION region** — none defined in the script body +- [ ] No external dependencies (no helpers, no config-loader, no `infrastructure.yml`) +- [ ] No emojis in script +- [ ] Includes inline verification command + +--- + +## Related Templates + +- **[Stage Index Template](./stage-index-template.mdx)** — Template for stage overview pages +- **[Provisioning Runbook Standard](./provisioning-runbook-standard.mdx)** — Framework definition +- **[Tab Selection Decision Logic](./tab-decision-logic.mdx)** — Flowchart for selecting correct tab profile + +--- + +**Version Control** + +- Created: 2026-01-30 by Product Technology Team +- Last Updated: 2026-03-10 by Product Technology Team +- Version: 5.0.0 diff --git a/standards/provisioning/tab-decision-logic.mdx b/standards/provisioning/tab-decision-logic.mdx new file mode 100644 index 000000000..4609b7605 --- /dev/null +++ b/standards/provisioning/tab-decision-logic.mdx @@ -0,0 +1,133 @@ +--- +title: "Tab Selection Decision Logic" +sidebar_label: "Tab Decision Logic" +sidebar_position: 4 +description: "Decision flowchart for selecting the correct execution option tabs in provisioning runbook documents" +--- + +# Tab Selection Decision Logic + +[![Standard](https://img.shields.io/badge/Type-Standard-purple?style=flat-square)](./provisioning-runbook-standard.mdx) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +> **DOCUMENT CATEGORY**: Standard +> **SCOPE**: All provisioning runbook task documents +> **PURPOSE**: Provide a decision flowchart and reference tables for selecting the correct tab structure per task + +**Status**: Active +**Last Updated**: 2026-03-10 + +--- + +This flowchart determines which tabs a provisioning task document requires based on its **execution target**. Every task must be classified before authoring tabs. + +**Master Reference**: [Provisioning Runbook Standard](./provisioning-runbook-standard.mdx) + +--- + +## Decision Flowchart + +```mermaid +flowchart TD + START["Does this task have more\nthan one execution method?"] + START -- NO --> NOTABS["NO TABS\nSingle procedure, narrative,\nvalidation, or checklist"] + START -- YES --> TARGET["What is the execution target?"] + + TARGET --> AZURE["AZURE ONLY\nPortal / Azure API operations"] + TARGET --> WINDOWS["WINDOWS SERVER\nOn-prem node, console/RDP"] + TARGET --> HARDWARE["PHYSICAL / VENDOR HARDWARE\niDRAC, switch, firewall, OpenGear"] + TARGET --> NETWORK["NETWORK DEVICE\nFortiGate, Dell switch"] + + AZURE --> AZ1["Tab 1: Azure Portal\nvalue=manual | default"] + AZURE --> AZ2["Tab 2: Azure CLI / PowerShell\nvalue=orchestrated\nReads infrastructure.yml"] + AZURE --> AZ3["Tab 3: Standalone Script\nvalue=standalone\n#region CONFIGURATION — all vars at top"] + + WINDOWS --> W_MANUAL{"Has a manual/GUI method?\nSConfig, ADUC, DNS Manager, WAC, etc."} + W_MANUAL -- YES --> W1["Tab 1: Contextual GUI label\nvalue=manual | default"] + W_MANUAL -- NO --> W2 + + W1 --> W2["Tab 2: Orchestrated Script - Mgmt Server\nvalue=orchestrated\nReads infrastructure.yml via PSRemoting"] + W2 --> W3["Tab 3: Standalone Script\nvalue=standalone\n#region CONFIGURATION — all vars at top"] + + HARDWARE --> H1["Tab 1: Vendor UI label\nvalue=manual | default\ne.g. iDRAC Web UI, Switch Console"] + HARDWARE --> H2{"API exists?"} + H2 -- YES --> H2a["Tab 2: API / CLI Script"] + HARDWARE --> H3{"Scriptable?"} + H3 -- YES --> H3a["Tab 3: Standalone Script\nvalue=standalone"] + + NETWORK --> N1["groupId=device-type\nTabs per device, NOT per execution method"] +``` + +--- + +## Consistent Tab Labels + +All task documents **must** use these exact label/value pairs. No variations. + +### Azure-Only Tasks + +| Tab | `value` | `label` | Config Source | +|-----|---------|---------|---------------| +| 1 (default) | `manual` | `Azure Portal` | N/A | +| 2 | `orchestrated` | `Azure CLI / PowerShell` | `infrastructure.yml` | +| 3 | `standalone` | `Standalone Script` | `#region CONFIGURATION` — all vars declared at top | + +**Key rule**: Azure tasks operate against the control plane — there is no target node. Use Standalone Script for copy-paste execution, Azure CLI / PowerShell for config-driven execution. + +### Windows Server Tasks + +| Tab | `value` | `label` | Config Source | +|-----|---------|---------|---------------| +| 1 (default) | `manual` | *Contextual* (SConfig, ADUC, WAC, etc.) | N/A | +| 2 | `orchestrated` | `Orchestrated Script (Mgmt Server)` | `infrastructure.yml` via PSRemoting | +| 3 | `standalone` | `Standalone Script` | `#region CONFIGURATION` — all vars declared at top | + +### Physical / Vendor Hardware Tasks + +| Tab | `value` | `label` | Config Source | +|-----|---------|---------|---------------| +| 1 (default) | `manual` | *Vendor-specific* (iDRAC Web UI, Switch Console, FortiGate GUI) | N/A | +| 2 (if API) | `api` | `API / CLI Script` | Varies | +| 3 (if scriptable) | `standalone` | `Standalone Script` | `#region CONFIGURATION` | + +### Network Devices + +Use `groupId="device-type"` with tabs per device model, **not** per execution method. + +### CI/CD Pipeline and Documentation-Only + +No tabs. These are not execution-option documents. + +--- + +## Default Tab Logic + +| Condition | Default Tab | +|-----------|-------------| +| Manual tab exists | Manual tab is default | +| No manual tab | Orchestrated Script is default | + +Set default using the `default` attribute on the ``. + +--- + +## groupId Values + +| Context | `groupId` | +|---------|-----------| +| Execution option tabs | `deployment-method` | +| Network device variant tabs | `device-type` | + +--- + +## Common Violations + +| Violation | Correction | +|-----------|------------| +| Azure-only task has no Orchestrated tab | Add `Azure CLI / PowerShell` tab (`value="orchestrated"`). | +| Azure-only task uses label `Orchestrated Script (Mgmt Server)` | Rename to `Azure CLI / PowerShell`. Keep `value=orchestrated`. | +| Windows task uses label `Azure CLI / PowerShell` | Rename to `Orchestrated Script (Mgmt Server)`. | +| Standalone Script tab has variables scattered in the script body | All variables must be declared in `#region CONFIGURATION` before any executable code. | +| Orchestrated Script reading hardcoded values instead of `infrastructure.yml` | Replace inline values with `$config.{path}` references. | +| Tabs on CI/CD or documentation-only pages | Remove tabs entirely. | +| Network device task with execution-method tabs | Use `groupId="device-type"` with per-device tabs instead. | diff --git a/standards/repo-structure.mdx b/standards/repo-structure.mdx new file mode 100644 index 000000000..909c25623 --- /dev/null +++ b/standards/repo-structure.mdx @@ -0,0 +1,262 @@ +--- +title: Repository Structure Standard +sidebar_label: Repository Structure +sidebar_position: 1 +description: "Required files, directory layout, labels, and branch naming conventions for all AzureLocal repositories." +--- + +# Repository Structure Standard + +[![Standard](https://img.shields.io/badge/Type-Standard-green?style=flat-square)](./index.mdx) +[![AzureLocal](https://img.shields.io/badge/AzureLocal-Community-0078D4?style=flat-square)](https://azurelocal.cloud) + +> **DOCUMENT CATEGORY**: Standard +> **SCOPE**: All AzureLocal repositories +> **PURPOSE**: Define the required structure every repository must follow + +**Status**: Active +**Last Updated**: 2026-03-17 + +--- + +## Required Files + +Every AzureLocal repository **must** contain all of these files at the root: + +| File | Purpose | +|------|---------| +| `README.md` | Project overview, quick-start, and link to central standards | +| `CONTRIBUTING.md` | Contribution guidelines — must link to [central standards](https://azurelocal.cloud/standards/) | +| `LICENSE` | License file (MIT) | +| `CHANGELOG.md` | Version history following [Keep a Changelog](https://keepachangelog.com/) | +| `release-please-config.json` | Release automation config | +| `.gitignore` | Must exclude `config/variables.yml`, `logs/`, `build/`, `site/` | +| `mkdocs.yml` | MkDocs Material site configuration (solution repos) | + +--- + +## Required Directories + +| Directory | Purpose | Required In | +|-----------|---------|-------------| +| `config/` | Configuration files | All solution repos | +| `config/variables.example.yml` | Variable template (never `variables.yml`) | All solution repos | +| `config/schema/variables.schema.json` | JSON Schema for CI validation | All solution repos | +| `docs/` | Documentation source | All repos | +| `docs/reference/variables.md` | Variable reference catalog | All solution repos | +| `.github/workflows/` | CI/CD workflows | All repos | +| `.github/PULL_REQUEST_TEMPLATE.md` | PR template with standards checklist | All repos | +| `.github/ISSUE_TEMPLATE/` | Issue templates (bug, feature, docs) | All repos | +| `scripts/` | PowerShell automation scripts | Repos with scripts | +| `tests/` | Pester tests | Repos with scripts | +| `logs/` | Runtime log output (`.gitignored`) | Repos with scripts | +| `styles/` | Vale linting styles | All repos | +| `project_management/` | Project tracking docs | All repos | + +--- + +## Directory Layout + +### Solution Repository (e.g., sofs-fslogix, avd, loadtools, vm-conversion) + +``` +repo-root/ +├── .github/ +│ ├── ISSUE_TEMPLATE/ +│ │ ├── bug_report.md +│ │ ├── feature_request.md +│ │ └── docs_issue.md +│ ├── PULL_REQUEST_TEMPLATE.md +│ └── workflows/ +│ └── validate-config.yml +├── config/ +│ ├── variables.example.yml +│ ├── variables.yml # .gitignored +│ └── schema/ +│ └── variables.schema.json +├── docs/ +│ ├── index.md +│ ├── reference/ +│ │ └── variables.md +│ └── ... +├── scripts/ +├── tests/ +├── logs/ # .gitignored +├── styles/ +├── project_management/ +├── CHANGELOG.md +├── CONTRIBUTING.md +├── LICENSE +├── README.md +├── mkdocs.yml +└── release-please-config.json +``` + +### Platform Toolkit Repository + +``` +azurelocal-toolkit/ +├── .github/ +│ ├── PULL_REQUEST_TEMPLATE.md +│ └── workflows/ +│ └── validate-config.yml +├── config/ +│ ├── variables.example.yml +│ ├── variables.yml # .gitignored +│ ├── infrastructure.yml +│ └── schema/ +│ ├── variables.schema.json +│ └── master-registry.yaml +├── configs/ # legacy — see config/ +├── docs/ +├── scripts/ +├── tests/ +├── tools/ +├── CHANGELOG.md +├── CONTRIBUTING.md +├── LICENSE +├── README.md +├── mkdocs.yml +└── release-please-config.json +``` + +--- + +## Branch Naming + +| Pattern | Usage | Example | +|---------|-------|---------| +| `main` | Default branch — always deployable | — | +| `feature/` | New features or enhancements | `feature/cloud-cache-support` | +| `fix/` | Bug fixes | `fix/ntfs-permissions` | +| `docs/` | Documentation changes | `docs/troubleshooting-guide` | +| `infra/` | CI/CD and infrastructure | `infra/add-pester-tests` | +| `feature/issue--` | Tied to a specific issue | `feature/issue-15-variable-standardization` | + +--- + +## Labels + +Every repository must have these labels: + +### Type Labels + +| Label | Color | Description | +|-------|-------|-------------| +| `type/feature` | `#1D76DB` | New feature or enhancement | +| `type/bug` | `#D73A4A` | Bug fix | +| `type/docs` | `#0075CA` | Documentation | +| `type/infra` | `#E4E669` | CI/CD, workflows, config | +| `type/refactor` | `#D4C5F9` | Code improvement, no behavior change | +| `type/security` | `#B60205` | Security fix or improvement | + +### Priority Labels + +| Label | Color | Description | +|-------|-------|-------------| +| `priority/critical` | `#B60205` | Must fix immediately | +| `priority/high` | `#D93F0B` | Important — current sprint | +| `priority/medium` | `#FBCA04` | Normal priority | +| `priority/low` | `#0E8A16` | Nice to have | + +### Solution Labels (org-wide) + +| Label | Color | Description | +|-------|-------|-------------| +| `solution/sofs` | `#5319E7` | SOFS + FSLogix | +| `solution/avd` | `#006B75` | Azure Virtual Desktop | +| `solution/loadtools` | `#1D76DB` | Load testing tools | +| `solution/vmconvert` | `#D4C5F9` | VM conversion toolkit | +| `solution/docs-site` | `#0075CA` | Community docs site | +| `solution/toolkit` | `#E4E669` | Platform toolkit | +| `solution/workspace` | `#C2E0C6` | Cross-repo / org-wide | + +--- + +## Commit Messages + +All repositories use [Conventional Commits](https://www.conventionalcommits.org/): + +``` +(): +``` + +| Type | When | +|------|------| +| `feat` | New feature | +| `fix` | Bug fix | +| `docs` | Documentation only | +| `infra` | CI/CD, workflows, config | +| `chore` | Maintenance | +| `refactor` | Code improvement, no behavior change | +| `test` | Tests | +| `style` | Formatting, no logic change | + +--- + +## CI Validation + +Every repository with a `config/` directory must include the `validate-config.yml` workflow. See the [Variable Management Standard](/docs/implementation/04-variable-management-standard) for details. + +Additionally, the **repo-structure validation** workflow checks for required files on every PR: + +```yaml +# .github/workflows/validate-repo-structure.yml +name: Validate Repo Structure +on: + pull_request: + branches: [main] + +jobs: + check-structure: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Check required files + run: | + missing=0 + for f in README.md CONTRIBUTING.md LICENSE CHANGELOG.md .gitignore; do + if [ ! -f "$f" ]; then + echo "::error::Missing required file: $f" + missing=$((missing + 1)) + fi + done + if [ $missing -gt 0 ]; then + echo "::error::$missing required file(s) missing" + exit 1 + fi + echo "All required files present" + + - name: Check required directories + run: | + missing=0 + for d in docs .github; do + if [ ! -d "$d" ]; then + echo "::error::Missing required directory: $d/" + missing=$((missing + 1)) + fi + done + if [ $missing -gt 0 ]; then + echo "::error::$missing required directory(s) missing" + exit 1 + fi + echo "All required directories present" + + - name: Check PR template exists + run: | + if [ ! -f ".github/PULL_REQUEST_TEMPLATE.md" ]; then + echo "::error::Missing .github/PULL_REQUEST_TEMPLATE.md" + exit 1 + fi + echo "PR template found" +``` + +--- + +## Related Standards + +- [Documentation Standards](./documentation/documentation-standards.mdx) — Writing and formatting conventions +- [Scripting Standards](./scripting/scripting-standards.mdx) — PowerShell conventions +- [Variable Management Standard](/docs/implementation/04-variable-management-standard) — Config file patterns +- [Fictional Company Policy](./fictional-company-policy.mdx) — IIC usage guidelines diff --git a/standards/scripting/bash-scripting-standards.md b/standards/scripting/bash-scripting-standards.md new file mode 100644 index 000000000..9a0cc53f7 --- /dev/null +++ b/standards/scripting/bash-scripting-standards.md @@ -0,0 +1,546 @@ +# Bash Scripting Standards for Azure Local Toolkit + +## Overview + +This document defines the coding standards and best practices for Bash scripts used in the Azure Local Toolkit. All Bash scripts interact with Azure CLI and must follow these conventions for consistency, maintainability, and reliability. + +**Key Requirements:** +- All scripts must read configuration from `infrastructure.yml` (never hardcoded values) +- All scripts must pass ShellCheck linting +- All scripts must use the standardized naming pattern (`az-verb-resource.sh`) +- All scripts must include proper error handling and logging + +**Related Standards:** +- [Scripting Standards](./scripting-standards.mdx) - PowerShell and overall scripting standards +- [Scripting Framework](./scripting-framework.mdx) - Config-driven script architecture +- [PowerShell Organization](./powershell-organization-standard.mdx) - Directory structure and organization + +## File Naming Conventions + +### Script Names + +**Azure CLI (Bash) scripts must follow this pattern:** +- Use **lowercase with hyphens** +- Follow the `az-verb-resource.sh` pattern +- Examples: + - `az-create-vnet.sh` + - `az-deploy-resources.sh` + - `az-get-subscription.sh` + - `az-test-connectivity.sh` + +**For remote/management-box execution scripts:** +- Prefix with `invoke-` (e.g., `invoke-deploy-cluster.sh`) + +### Common Verbs +| Verb | Usage | +|------|-------| +| `create` | Create new resources | +| `set` | Configure or update existing resources | +| `get` | Retrieve information | +| `test` | Validate or verify | +| `delete` | Delete resources | +| `invoke` | Execute an action (especially for remote scripts) | +| `sync` | Synchronize data | + +## File Structure + +### Required Header + +Every Bash script must include this header template: + +```bash +#!/bin/bash +#=============================================================================== +# Script Name: script-name.sh +# Description: Brief description of what the script does +# Author: TierPoint ProdTech Team +# Created: YYYY-MM-DD +# Modified: YYYY-MM-DD +# Version: 1.0.0 +# +# Dependencies: +# - Azure CLI (az) version 2.50.0+ +# - jq for JSON processing +# +# Usage: +# ./script-name.sh [options] +# +# Parameters: +# -c, --config Path to configuration file (required) +# -e, --environment Environment name (dev|staging|prod) +# -v, --verbose Enable verbose output +# -h, --help Show this help message +# +# Exit Codes: +# 0 - Success +# 1 - General error +# 2 - Invalid arguments +# 3 - Azure CLI error +# 4 - Configuration error +#=============================================================================== +``` + +### Script Sections + +Organize scripts in this order: + +```bash +#!/bin/bash +# Header (as above) + +#=============================================================================== +# CONFIGURATION +#=============================================================================== +set -euo pipefail +IFS=$'\n\t' + +# Script directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" + +# Default values +VERBOSE=false +DRY_RUN=false +CONFIG_FILE="" +ENVIRONMENT="" + +#=============================================================================== +# LOGGING FUNCTIONS +#=============================================================================== +log_info() { + echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') - $*" +} + +log_warn() { + echo "[WARN] $(date '+%Y-%m-%d %H:%M:%S') - $*" >&2 +} + +log_error() { + echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') - $*" >&2 +} + +log_debug() { + if [[ "$VERBOSE" == true ]]; then + echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') - $*" + fi +} + +#=============================================================================== +# HELPER FUNCTIONS +#=============================================================================== +show_help() { + grep '^#' "$0" | grep -E '^# (Usage|Parameters|Exit)' -A 100 | head -20 +} + +check_dependencies() { + local deps=("az" "jq") + for dep in "${deps[@]}"; do + if ! command -v "$dep" &> /dev/null; then + log_error "Required dependency '$dep' is not installed" + exit 1 + fi + done +} + +check_azure_login() { + if ! az account show &> /dev/null; then + log_error "Not logged in to Azure CLI. Run 'az login' first." + exit 3 + fi +} + +#=============================================================================== +# CONFIGURATION LOADING +#=============================================================================== +load_config() { + local config_file="$1" + + if [[ ! -f "$config_file" ]]; then + log_error "Configuration file not found: $config_file" + exit 4 + fi + + # Load YAML config using yq or parse manually + log_info "Loading configuration from: $config_file" +} + +#=============================================================================== +# ARGUMENT PARSING +#=============================================================================== +parse_arguments() { + while [[ $# -gt 0 ]]; do + case "$1" in + -c|--config) + CONFIG_FILE="$2" + shift 2 + ;; + -e|--environment) + ENVIRONMENT="$2" + shift 2 + ;; + -v|--verbose) + VERBOSE=true + shift + ;; + --dry-run) + DRY_RUN=true + shift + ;; + -h|--help) + show_help + exit 0 + ;; + *) + log_error "Unknown option: $1" + show_help + exit 2 + ;; + esac + done +} + +validate_arguments() { + if [[ -z "$CONFIG_FILE" ]]; then + log_error "Configuration file is required. Use -c or --config" + exit 2 + fi + + if [[ -z "$ENVIRONMENT" ]]; then + log_error "Environment is required. Use -e or --environment" + exit 2 + fi +} + +#=============================================================================== +# MAIN FUNCTIONS +#=============================================================================== +# Add your main logic functions here + +#=============================================================================== +# MAIN EXECUTION +#=============================================================================== +main() { + log_info "Starting $SCRIPT_NAME" + + parse_arguments "$@" + validate_arguments + check_dependencies + check_azure_login + load_config "$CONFIG_FILE" + + # Main script logic here + + log_info "Completed $SCRIPT_NAME" +} + +# Run main function +main "$@" +``` + +## Error Handling + +### Required Settings + +Always include these settings at the top of scripts: + +```bash +set -euo pipefail +IFS=$'\n\t' +``` + +| Setting | Purpose | +|---------|---------| +| `-e` | Exit immediately on error | +| `-u` | Treat unset variables as errors | +| `-o pipefail` | Pipeline fails if any command fails | +| `IFS=$'\n\t'` | Safer word splitting | + +### Error Trapping + +Use trap for cleanup and error reporting: + +```bash +cleanup() { + local exit_code=$? + # Cleanup temporary files + if [[ -n "${TEMP_DIR:-}" && -d "$TEMP_DIR" ]]; then + rm -rf "$TEMP_DIR" + fi + exit $exit_code +} + +trap cleanup EXIT + +error_handler() { + local line_number=$1 + local command="$2" + log_error "Command '$command' failed at line $line_number" +} + +trap 'error_handler ${LINENO} "$BASH_COMMAND"' ERR +``` + +## Azure CLI Best Practices + +### Consistent Output Format + +Always use JSON output for parsing: + +```bash +# Good - explicit JSON output +result=$(az group list --output json) + +# Parse with jq +resource_group=$(echo "$result" | jq -r '.[0].name') +``` + +### Resource Creation Pattern + +```bash +create_resource_group() { + local name="$1" + local location="$2" + local tags="$3" + + log_info "Creating resource group: $name in $location" + + if az group show --name "$name" &> /dev/null; then + log_warn "Resource group '$name' already exists" + return 0 + fi + + if [[ "$DRY_RUN" == true ]]; then + log_info "[DRY-RUN] Would create resource group: $name" + return 0 + fi + + az group create \ + --name "$name" \ + --location "$location" \ + --tags $tags \ + --output json + + log_info "Created resource group: $name" +} +``` + +### Subscription Context + +Always explicitly set subscription context: + +```bash +set_subscription() { + local subscription_id="$1" + + log_info "Setting subscription context: $subscription_id" + az account set --subscription "$subscription_id" + + # Verify + current=$(az account show --query id --output tsv) + if [[ "$current" != "$subscription_id" ]]; then + log_error "Failed to set subscription context" + exit 3 + fi +} +``` + +## Variable Conventions + +### Naming + +- Use **UPPERCASE** for environment variables and constants +- Use **lowercase** for local variables +- Use **snake_case** for multi-word variables + +```bash +# Constants (uppercase) +readonly AZURE_LOCATION="eastus2" +readonly MAX_RETRIES=3 + +# Environment variables (uppercase) +export ARM_SUBSCRIPTION_ID="${ARM_SUBSCRIPTION_ID:-}" + +# Local variables (lowercase with underscores) +local resource_group_name="" +local deployment_name="" +``` + +### Configuration Variables + +Reference variables from config files using consistent patterns: + +```bash +# Load from YAML config +get_config_value() { + local key="$1" + local config_file="$2" + + # Using yq + yq eval ".$key" "$config_file" +} + +# Example usage +location=$(get_config_value "azure.location" "$CONFIG_FILE") +environment=$(get_config_value "deployment.environment" "$CONFIG_FILE") +``` + +## ShellCheck Compliance + +All scripts must pass ShellCheck with no warnings. Common rules: + +### SC2086 - Quote Variables +```bash +# Bad +echo $variable + +# Good +echo "$variable" +``` + +### SC2155 - Separate Declaration and Assignment +```bash +# Bad +local result=$(some_command) + +# Good +local result +result=$(some_command) +``` + +### SC2034 - Unused Variables +```bash +# Mark intentionally unused variables +# shellcheck disable=SC2034 +UNUSED_VAR="intentionally unused" +``` + +### Running ShellCheck + +```bash +# Check single file +shellcheck script-name.sh + +# Check all scripts +find scripts/ -name "*.sh" -exec shellcheck {} \; + +# With specific severity +shellcheck --severity=warning script-name.sh +``` + +## Testing + +### Test Script Structure + +Create companion test files using the pattern `test-.sh`: + +```bash +#!/bin/bash +# test-new-resource-group.sh + +source "./test-helpers.sh" + +test_create_resource_group_success() { + # Arrange + local name="test-rg-$$" + local location="eastus2" + + # Act + ./new-resource-group.sh --name "$name" --location "$location" + local result=$? + + # Assert + assert_equals 0 $result "Should create resource group successfully" + + # Cleanup + az group delete --name "$name" --yes --no-wait +} + +# Run tests +run_tests +``` + +## Integration with PowerShell Scripts + +When Bash scripts work alongside PowerShell scripts: + +1. Use the same configuration files (YAML) +2. Follow parallel naming conventions: + - PowerShell: `New-ResourceGroup.ps1` + - Bash: `new-resource-group.sh` +3. Use consistent exit codes +4. Log to the same log directory structure + +## Documentation + +### Inline Comments + +```bash +# Single-line comments for brief explanations +variable="value" # Inline comment for specific lines + +#------------------------------------------------------------------------------- +# Section comments for logical groupings +# Use multiple lines for complex explanations +#------------------------------------------------------------------------------- +``` + +### Function Documentation + +```bash +####################################### +# Creates a new Azure resource group +# Globals: +# AZURE_LOCATION - Default Azure region +# Arguments: +# $1 - Resource group name (required) +# $2 - Location (optional, defaults to AZURE_LOCATION) +# Outputs: +# Writes resource group JSON to stdout +# Returns: +# 0 if successful, non-zero on error +####################################### +create_resource_group() { + local name="$1" + local location="${2:-$AZURE_LOCATION}" + # Implementation +} +``` + +## Version Control + +### Pre-commit Checks + +Add to `.pre-commit-config.yaml`: + +```yaml +repos: + - repo: https://github.com/shellcheck-py/shellcheck-py + rev: v0.9.0.5 + hooks: + - id: shellcheck + args: ["--severity=warning"] +``` + +### Changelog + +Maintain version history in script headers and update when making changes. + +--- + +## Quick Reference + +| Aspect | Standard | +|--------|----------| +| File naming | `lowercase-with-hyphens.sh` | +| Variables (const) | `UPPERCASE_SNAKE_CASE` | +| Variables (local) | `lowercase_snake_case` | +| Functions | `lowercase_snake_case()` | +| Error handling | `set -euo pipefail` | +| Output format | JSON with jq parsing | +| Linting | ShellCheck with no warnings | + +--- + +*Document Version: 1.0.0* +*Last Updated: 2026-02-01* +*Maintained by: TierPoint ProdTech Team* diff --git a/standards/scripting/index.mdx b/standards/scripting/index.mdx new file mode 100644 index 000000000..cdbfd52a3 --- /dev/null +++ b/standards/scripting/index.mdx @@ -0,0 +1,84 @@ +--- +title: Scripting Standards +sidebar_label: Scripting +sidebar_position: 3 +description: "PowerShell scripting standards, frameworks, and organization patterns" +--- + +# Scripting Standards + +[![PowerShell](https://img.shields.io/badge/Type-PowerShell-purple?style=flat-square&logo=powershell)](./scripting-standards.mdx) +[![Standards](https://img.shields.io/badge/Standards-Documentation-orange?style=flat-square)](https://github.com/kristopherjturner/master-workplace-project) + +> **DOCUMENT CATEGORY**: Standards Index +> **SCOPE**: PowerShell scripting standards, frameworks, and organization patterns +> **PURPOSE**: Define coding standards for all PowerShell automation scripts +> **AUDIENCE**: Anyone writing PowerShell scripts in this documentation repository + +**Status**: Active +**Applies To**: All PowerShell scripts across all product documentation +**Last Updated**: 2026-02-08 + +--- + +## What Is This? + +This section defines how to write PowerShell scripts for any product documented in this repository. It covers code style, organization, error handling, and automation patterns that make scripts reliable, maintainable, and easy to understand. + +## Why Does This Matter? + +**Without standards**, scripts become: +- Hard to read and understand +- Difficult to troubleshoot when they fail +- Impossible to maintain as requirements change +- Unreliable across different environments + +**With these standards**, you get: +- **Consistent code** - Every script follows the same patterns +- **Clear errors** - Problems are easy to diagnose and fix +- **Reusable automation** - Same scripts work across products +- **Easy maintenance** - New engineers can understand and update code + +## How Does This Help Engineers? + +1. **Faster Troubleshooting**: Consistent error handling and logging means you quickly find issues +2. **Reliable Automation**: Tested patterns reduce script failures +3. **Knowledge Transfer**: New team members can read and understand scripts immediately +4. **Time Savings**: Reusable patterns mean less code to write and test + +## Standards in This Section + +import DocCardList from '@theme/DocCardList'; + + + +## Quick Reference + +| Standard | Purpose | When To Use | +|----------|---------|-------------| +| [Scripting Standards](./scripting-standards.mdx) | Core PowerShell coding rules | Writing any new script | +| [Scripting Framework](./scripting-framework.mdx) | Architecture for complex scripts | Multi-step deployments | +| [PowerShell Organization](./powershell-organization-standard.mdx) | File and folder structure | Organizing scripts in toolkit | + +## Key Principles + +1. **Consistency** - Same patterns across all scripts +2. **Readability** - Clear variable names, proper comments, logical flow +3. **Maintainability** - Modular design, error handling, logging +4. **Configuration-Driven** - Scripts read from config files, not hardcoded values + +## How To Maintain Standards + +**For Script Authors:** +- Follow these standards when writing new scripts +- Update existing scripts to match standards during maintenance +- Report unclear or missing guidelines to documentation team + +**For Script Reviewers:** +- Check new scripts against these standards +- Provide feedback on inconsistencies +- Suggest standard updates based on experience + +--- + +*For questions about these standards, contact the documentation team.* diff --git a/standards/scripting/powershell-organization-standard.mdx b/standards/scripting/powershell-organization-standard.mdx new file mode 100644 index 000000000..81de34257 --- /dev/null +++ b/standards/scripting/powershell-organization-standard.mdx @@ -0,0 +1,963 @@ +--- +id: powershell-organization-standard +title: PowerShell Organization Standard +sidebar_label: PowerShell Organization Standard +--- +# PowerShell Organization Standard + +[![Standards](https://img.shields.io/badge/Type-Standards-purple?style=flat-square)](https://docs.microsoft.com/en-us/powershell/) +[![PowerShell](https://img.shields.io/badge/PowerShell-Organization-blue?style=flat-square&logo=powershell)](https://docs.microsoft.com/en-us/powershell/) +[![Best-Practices](https://img.shields.io/badge/Category-Best_Practices-green?style=flat-square)](https://docs.microsoft.com/en-us/powershell/) + +> **DOCUMENT CATEGORY**: Standards +> **SCOPE**: Folder structure and organization patterns for PowerShell codebases +> **PURPOSE**: Define standard directory structure and organization for PowerShell scripts, modules, and automation +> **MASTER REFERENCE**: [Scripting Standards](./scripting-standards.mdx) + +**Quick Links:** [Scripting Standards](./scripting-standards.mdx) • [Scripting Framework](./scripting-framework.mdx) + +--- + +## Overview + +This document defines the standard folder structure and organization patterns for PowerShell codebases across Product Technology repositories. Consistent organization improves maintainability, discoverability, and collaboration across multiple automation projects. + +## Why Organization Matters + +### Benefits of Standard Structure + +- **Discoverability**: Developers instantly know where to find scripts +- **Maintainability**: Clear separation of concerns simplifies updates +- **Onboarding**: New team members understand structure immediately +- **Consistency**: Same patterns across all repositories +- **Reusability**: Modules and utilities easy to share +- **Testing**: Clear location for test files +- **CI/CD**: Predictable structure enables automation + +### Problems Without Organization + +Without proper organization: + +- Scripts scattered across repository with no clear purpose +- Duplicate functionality in multiple locations +- Unclear which scripts are production vs development +- No separation between deployment and utility scripts +- Difficult to locate specific functionality +- Hard to maintain and update scripts +- Testing becomes chaotic + +## Standard Folder Structure + +### Top-Level Structure + +All PowerShell code should be organized under a `src/powershell/` or `scripts/` directory at the repository root: + +``` +repository-root/ +├── src/ +│ └── powershell/ # All PowerShell code here +│ ├── deploy/ # Deployment automation +│ ├── utilities/ # Utility and helper scripts +│ ├── modules/ # PowerShell modules +│ ├── ci-cd/ # CI/CD pipeline scripts +│ ├── tests/ # Pester test files +│ ├── docs/ # PowerShell-specific documentation +│ └── README.md # PowerShell directory overview +``` + +**or** + +``` +repository-root/ +├── scripts/ # All PowerShell code here (simpler repos) +│ ├── deploy/ +│ ├── utilities/ +│ ├── modules/ +│ └── README.md +``` + +### Folder Purposes + +| Folder | Purpose | Contents | Example | +|--------|---------|----------|---------| +| **deploy/** | Deployment automation | Scripts that deploy infrastructure, configure environments, provision resources | `Deploy-Infrastructure.ps1`, `Deploy-KeyVault.ps1` | +| **utilities/** | Helper scripts | Reusable utilities, management tools, maintenance scripts | `Test-Connectivity.ps1`, `Get-EnvironmentInfo.ps1` | +| **modules/** | PowerShell modules | .psm1 modules with reusable functions | `AzureHelpers.psm1`, `LoggingModule.psm1` | +| **ci-cd/** | CI/CD scripts | Pipeline scripts, build automation, release scripts | `Build.ps1`, `Test-Pipeline.ps1`, `Publish.ps1` | +| **tests/** | Pester tests | Unit and integration tests for scripts and modules | `Deploy-Infrastructure.Tests.ps1` | +| **docs/** | Documentation | PowerShell-specific documentation, usage guides | `deployment-guide.md`, `module-reference.md` | + +## Folder Details + +### deploy/ + +**Purpose**: Scripts that deploy, configure, or provision infrastructure and resources. + +**Organization**: + +``` +deploy/ +├── platform/ # Platform-level deployments +│ ├── Deploy-Platform-Foundation.ps1 +│ └── README.md +├── keyvault/ # Key Vault deployments +│ ├── Deploy-KeyVault.ps1 +│ ├── Deploy-KeyVault-Monitoring.ps1 +│ └── README.md +├── tenant-onboarding/ # Tenant onboarding scripts +│ ├── Add-TenantToKeyVault.ps1 +│ ├── Test-TenantKeyVaultAccess.ps1 +│ └── README.md +├── networking/ # Network deployments +│ ├── Deploy-VNet.ps1 +│ ├── Deploy-PrivateEndpoint.ps1 +│ └── README.md +└── README.md # Deployment overview +``` + +**Guidelines**: + +- Group related deployment scripts in subfolders +- Each subfolder should have a README.md +- Use clear, action-oriented script names: `Deploy-*`, `Configure-*`, `Provision-*` +- Include validation and rollback logic +- Document prerequisites and dependencies + +**Naming Patterns**: + +- `Deploy-.ps1` - Deploy specific resource +- `Configure-.ps1` - Configure service settings +- `Provision-.ps1` - Full environment provisioning +- `Remove-.ps1` - Cleanup/removal scripts + +### utilities/ + +**Purpose**: Reusable helper scripts, management tools, and maintenance automation. + +**Organization**: + +``` +utilities/ +├── spn-management/ # Service principal management +│ ├── New-ServicePrincipal.ps1 +│ ├── Test-SPNAccess.ps1 +│ └── README.md +├── monitoring/ # Monitoring utilities +│ ├── Get-ResourceHealth.ps1 +│ ├── Test-Connectivity.ps1 +│ └── README.md +├── reporting/ # Report generation +│ ├── Get-CostReport.ps1 +│ ├── Export-Configuration.ps1 +│ └── README.md +├── maintenance/ # Maintenance scripts +│ ├── Cleanup-OldResources.ps1 +│ ├── Update-Tags.ps1 +│ └── README.md +└── README.md # Utilities overview +``` + +**Guidelines**: + +- Group utilities by functional area +- Include `Get-*`, `Test-*`, `Update-*`, `Export-*` verb patterns +- Utilities should be non-destructive by default +- Include `-WhatIf` support for operations that modify state +- Document parameters and return values clearly + +**Naming Patterns**: + +- `Get-.ps1` - Retrieve information +- `Test-.ps1` - Test/validate conditions +- `Update-.ps1` - Update existing resources +- `Export-.ps1` - Export data/reports +- `Sync-.ps1` - Synchronize resources + +### modules/ + +**Purpose**: PowerShell modules (.psm1) containing reusable functions. + +**Organization**: + +``` +modules/ +├── AzureHelpers/ # Azure helper functions +│ ├── AzureHelpers.psm1 +│ ├── AzureHelpers.psd1 # Module manifest +│ ├── Public/ # Exported functions +│ │ ├── Get-AzureResource.ps1 +│ │ └── Test-AzureConnection.ps1 +│ ├── Private/ # Internal helper functions +│ │ └── Invoke-AzureAPI.ps1 +│ ├── Tests/ # Module tests +│ │ └── AzureHelpers.Tests.ps1 +│ └── README.md +├── LoggingModule/ # Logging utilities +│ ├── LoggingModule.psm1 +│ ├── LoggingModule.psd1 +│ └── README.md +└── README.md # Modules overview +``` + +**Guidelines**: + +- Each module in its own subfolder +- Include module manifest (.psd1) for versioning +- Separate Public (exported) and Private (internal) functions +- Include Pester tests for modules +- Document all exported functions with comment-based help +- Follow [PowerShell Module Structure](https://docs.microsoft.com/en-us/powershell/scripting/developer/module/writing-a-windows-powershell-module) + +**Module Structure Pattern**: + +```powershell +# ModuleName.psm1 +# Load private functions +$PrivateFunctions = Get-ChildItem -Path "$PSScriptRoot/Private/*.ps1" -ErrorAction SilentlyContinue +foreach ($Function in $PrivateFunctions) { + . $Function.FullName +} + +# Load public functions +$PublicFunctions = Get-ChildItem -Path "$PSScriptRoot/Public/*.ps1" -ErrorAction SilentlyContinue +foreach ($Function in $PublicFunctions) { + . $Function.FullName +} + +# Export public functions +Export-ModuleMember -Function $PublicFunctions.BaseName +``` + +### ci-cd/ + +**Purpose**: Scripts specific to CI/CD pipelines, build automation, and release processes. + +**Organization**: + +``` +ci-cd/ +├── build/ # Build scripts +│ ├── Build.ps1 +│ ├── Restore-Dependencies.ps1 +│ └── README.md +├── test/ # Test automation +│ ├── Invoke-AllTests.ps1 +│ ├── Test-Coverage.ps1 +│ └── README.md +├── release/ # Release automation +│ ├── Publish-Module.ps1 +│ ├── Create-Release.ps1 +│ └── README.md +├── validation/ # Pre-deployment validation +│ ├── Test-Configuration.ps1 +│ ├── Validate-Environment.ps1 +│ └── README.md +└── README.md # CI/CD overview +``` + +**Guidelines**: + +- Scripts should be idempotent (safe to run multiple times) +- Include clear exit codes (0 = success, non-zero = failure) +- Log verbosely for troubleshooting +- Support common CI/CD parameters (e.g., `-BuildConfiguration`, `-Version`) +- Document pipeline integration requirements + +**Common CI/CD Scripts**: + +- `Build.ps1` - Compile/prepare artifacts +- `Test.ps1` - Run all tests +- `Publish.ps1` - Publish artifacts/modules +- `Deploy.ps1` - Deploy to environment +- `Validate.ps1` - Pre/post-deployment validation + +### tests/ + +**Purpose**: Pester test files for unit and integration testing. + +**Organization**: + +``` +tests/ +├── Unit/ # Unit tests +│ ├── Deploy-Infrastructure.Tests.ps1 +│ ├── Get-EnvironmentInfo.Tests.ps1 +│ └── README.md +├── Integration/ # Integration tests +│ ├── End-to-End.Tests.ps1 +│ ├── KeyVault-Integration.Tests.ps1 +│ └── README.md +├── Fixtures/ # Test fixtures and mocks +│ ├── Mock-AzureConnection.ps1 +│ └── Test-Data.json +└── README.md # Testing overview +``` + +**Guidelines**: + +- Mirror the source structure in test folder +- Test files named `.Tests.ps1` +- Use Pester framework for all tests +- Include both unit and integration tests +- Mock external dependencies in unit tests +- Document test prerequisites and setup + +**Test Naming Pattern**: + +``` +Source: deploy/keyvault/Deploy-KeyVault.ps1 +Test: tests/Unit/Deploy-KeyVault.Tests.ps1 +``` + +### docs/ + +**Purpose**: PowerShell-specific documentation, usage guides, and module references. + +**Organization**: + +``` +docs/ +├── deployment/ # Deployment documentation +│ ├── keyvault-deployment-guide.md +│ ├── networking-deployment-guide.md +│ └── README.md +├── utilities/ # Utility documentation +│ ├── monitoring-tools.md +│ ├── reporting-tools.md +│ └── README.md +├── modules/ # Module reference documentation +│ ├── azure-helpers-reference.md +│ ├── logging-module-reference.md +│ └── README.md +└── README.md # Documentation index +``` + +**Guidelines**: + +- Mirror folder structure of scripts +- Include usage examples for all scripts +- Document parameters, return values, and error conditions +- Provide troubleshooting sections +- Link to related scripts and modules + +## Naming Conventions + +### Script Files + +Follow **PascalCase** for all PowerShell script files: + +``` +Correct: +Deploy-Infrastructure.ps1 +Get-EnvironmentInfo.ps1 +Test-Connectivity.ps1 + +Incorrect: +deploy-infrastructure.ps1 +get_environment_info.ps1 +testConnectivity.ps1 +``` + +### PowerShell Verb-Noun Pattern + +Use approved PowerShell verbs with clear nouns: + +```powershell +# Common approved verbs: +Get-* # Retrieve data +Set-* # Modify data +New-* # Create new resource +Remove-* # Delete resource +Test-* # Validate/test condition +Invoke-* # Execute operation +Update-* # Update existing resource +Export-* # Export data +Import-* # Import data +Start-* # Start process/service +Stop-* # Stop process/service +Deploy-* # Deploy infrastructure (non-standard but common in DevOps) +``` + +**Get approved verbs**: + +```powershell +Get-Verb | Sort-Object Verb +``` + +### Folder Naming + +- Use **lowercase with hyphens** for subfolders: `spn-management/`, `tenant-onboarding/` +- Use **singular nouns** for category folders: `module/`, `utility/`, `test/` +- Use **descriptive names** that indicate purpose: `cross-tenant-sync/`, `monitoring/` + +## README.md Requirements + +Every PowerShell directory and subfolder should have a README.md documenting: + +### Root PowerShell README.md + +```markdown +# PowerShell Scripts + +> Brief description of PowerShell automation in this repository + +## Directory Structure + +[Folder structure with descriptions] + +## Getting Started + +[Prerequisites and setup instructions] + +## Usage + +[Common usage patterns] + +## Development + +[Development workflow] + +## Standards + +[Link to scripting standards and framework] +``` + +### Subfolder README.md + +```markdown +# [Folder Name] + +> Purpose of scripts in this folder + +## Scripts + +| Script | Description | Usage | +|--------|-------------|-------| +| Script1.ps1 | Description | `.\Script1.ps1 -Param1 Value` | + +## Prerequisites + +[Requirements] + +## Examples + +[Usage examples] +``` + +## Real-World Example: CMP Repository + +### Complete Structure + +``` +src/powershell/ +├── audit/ # Compliance and audit scripts +│ ├── Audit-Environment-Comprehensive.ps1 +│ ├── Audit-TenantSPNsAndApps.ps1 +│ └── README.md +├── automation/ # Azure Automation scripts +│ ├── Azure-Authentication.ps1 +│ ├── Deploy-AutomationAccount.ps1 +│ ├── Deploy-TargetTenantDynamicGroups.ps1 +│ ├── Sync-GroupMembershipToAttributes.ps1 +│ ├── Test-AutomationWorkflow.ps1 +│ └── README.md +├── ci-cd/ # CI/CD pipeline scripts +│ └── README.md +├── deploy/ # Deployment automation +│ ├── cross-tenant-sync/ # CTS deployment scripts +│ │ ├── Deploy-CrossTenantSync.ps1 +│ │ └── README.md +│ ├── environment-tenants/ # Tenant-specific deployments +│ │ ├── Deploy-SiteA.ps1 +│ │ ├── Deploy-SiteB.ps1 +│ │ └── README.md +│ ├── keyvault/ # Key Vault deployments +│ │ ├── Deploy-KeyVault.ps1 +│ │ ├── Deploy-KeyVault-Foundation.ps1 +│ │ ├── Deploy-KeyVault-Monitoring.ps1 +│ │ ├── Test-KeyVault-Access.ps1 +│ │ └── README.md +│ ├── platform/ # Platform foundation +│ │ ├── Deploy-Platform-Foundation.ps1 +│ │ └── README.md +│ ├── tenant-onboarding/ # Tenant onboarding +│ │ ├── Add-TenantToKeyVault.ps1 +│ │ ├── Test-TenantKeyVaultAccess.ps1 +│ │ ├── Migrate-SPN-Secrets.ps1 +│ │ └── README.md +│ └── README.md +├── management-groups/ # Management group operations +│ ├── Audit-ManagementGroups.ps1 +│ ├── Deploy-AllManagementGroups.ps1 +│ ├── New-ProdTechManagementGroups.ps1 +│ └── README.md +├── modules/ # PowerShell modules +│ ├── PartnerCenterSubscription.psm1 +│ └── README.md +├── spn-multi-tenant-setup/ # SPN multi-tenant configuration +│ ├── Assign-SPN-Permissions.ps1 +│ ├── Complete-SPN-Implementation.ps1 +│ ├── Configure-CertificateAuth.ps1 +│ ├── New-ServicePrincipal.ps1 +│ └── README.md +├── utilities/ # Utility scripts +│ ├── spn-management/ # SPN management utilities +│ │ ├── REVIEW_CHECKLIST.md +│ │ └── README.md +│ └── README.md +└── README.md # Root PowerShell README +``` + +### Key Observations + +1. **Clear functional separation**: deploy/ vs utilities/ vs modules/ +2. **Logical subfolder grouping**: keyvault/, tenant-onboarding/, environment-tenants/ +3. **README.md at every level**: Comprehensive documentation +4. **Consistent naming**: PascalCase scripts, lowercase-hyphen folders +5. **Purpose-driven organization**: Easy to find scripts by purpose + +## Anti-Patterns to Avoid + +### Flat Structure (Anti-Pattern) + +``` +powershell/ +├── Script1.ps1 +├── Script2.ps1 +├── Script3.ps1 +├── Script4.ps1 +├── ... (50+ scripts) +└── README.md +``` + +**Problems**: No organization, difficult to find scripts, no clear purpose + +### Inconsistent Naming (Anti-Pattern) + +``` +powershell/ +├── Deploy-KeyVault.ps1 # PascalCase +├── get-environment-info.ps1 # lowercase +├── testConnectivity.ps1 # camelCase +└── Update_Tags.ps1 # snake_case with underscores +``` + +**Problems**: Hard to predict file names, looks unprofessional + +### Over-Nesting (Anti-Pattern) + +``` +powershell/ +├── scripts/ +│ ├── deployment/ +│ │ ├── azure/ +│ │ │ ├── infrastructure/ +│ │ │ │ ├── keyvault/ +│ │ │ │ │ └── Deploy-KeyVault.ps1 +``` + +**Problems**: Too many folders, hard to navigate, unnecessary complexity + +### No Documentation (Anti-Pattern) + +``` +powershell/ +├── deploy/ +│ ├── Script1.ps1 +│ ├── Script2.ps1 +│ └── Script3.ps1 +├── utilities/ +│ ├── Script4.ps1 +│ └── Script5.ps1 +└── # No README.md files +``` + +**Problems**: No documentation, purpose unclear, no usage examples + +## Migration Guide + +### Migrating Existing Scripts + +If you have existing scripts that don't follow this standard: + +1. **Audit current structure**: Document what exists +2. **Map to standard folders**: Determine where scripts should go +3. **Create folder structure**: Build standard folders +4. **Move scripts**: Relocate to appropriate folders +5. **Rename if needed**: Update to PascalCase if necessary +6. **Add README.md files**: Document each folder +7. **Update references**: Fix any hard-coded paths +8. **Test**: Validate all scripts still work + +**Example migration**: + +``` +Before: +scripts/ +├── deploy_keyvault.ps1 +├── test_connectivity.ps1 +├── update-tags.ps1 +└── reporting_script.ps1 + +After: +src/powershell/ +├── deploy/ +│ └── keyvault/ +│ ├── Deploy-KeyVault.ps1 +│ └── README.md +├── utilities/ +│ ├── monitoring/ +│ │ ├── Test-Connectivity.ps1 +│ │ └── README.md +│ ├── maintenance/ +│ │ ├── Update-Tags.ps1 +│ │ └── README.md +│ └── reporting/ +│ ├── Get-CostReport.ps1 +│ └── README.md +└── README.md +``` + +## Integration with Standards + +### Reference Documents + +This organization standard works together with: + +- **[Scripting Standards](./scripting-standards.mdx)**: Coding standards for individual scripts + - Comment-based help requirements + - Parameter validation + - Error handling patterns + - Logging standards + +- **[Scripting Framework](./scripting-framework.mdx)**: Script templates and patterns + - Simple script template + - Advanced script template + - Module structure + - CI/CD integration + +- **[Documentation Standards](../documentation/documentation-standards.mdx)**: Documentation requirements + - README.md structure + - Comment standards + - Markdown formatting + +### Workflow Integration + +1. **Create repository**: Use Folder Structure Standard for top-level organization +2. **Create PowerShell folder**: Use this standard for PowerShell organization +3. **Write scripts**: Follow Scripting Standards for code quality +4. **Use templates**: Apply Scripting Framework templates +5. **Document**: Add README.md files at each level +6. **Test**: Create Pester tests in tests/ folder +7. **Integrate CI/CD**: Use ci-cd/ scripts in pipelines + +## Best Practices + +### 1. Start with README.md + +**Do this**: + +``` +# Create README.md before writing scripts +cd src/powershell/deploy/keyvault/ +New-Item README.md +# Document purpose, prerequisites, usage +# Then create scripts +``` + +**Don't do this**: + +``` +# Create scripts first, forget documentation +New-Item Deploy-KeyVault.ps1 +# (never get around to documentation) +``` + +### 2. Group Related Scripts + +**Do this**: + +``` +deploy/ +└── keyvault/ + ├── Deploy-KeyVault.ps1 + ├── Deploy-KeyVault-Foundation.ps1 + ├── Deploy-KeyVault-Monitoring.ps1 + ├── Test-KeyVault-Access.ps1 + └── README.md +``` + +**Don't do this**: + +``` +deploy/ +├── Deploy-KeyVault.ps1 +├── Deploy-VNet.ps1 +├── Deploy-KeyVault-Foundation.ps1 +├── Deploy-Storage.ps1 +├── Deploy-KeyVault-Monitoring.ps1 +└── README.md +``` + +### 3. Use Descriptive Folder Names + +**Do this**: + +``` +utilities/ +├── spn-management/ +├── tenant-onboarding/ +├── cross-tenant-sync/ +└── monitoring/ +``` + +**Don't do this**: + +``` +utilities/ +├── misc/ +├── stuff/ +├── other/ +└── tools/ +``` + +### 4. Maintain Consistent Depth + +**Do this**: + +``` +# Consistent 2-3 level depth +deploy/ +├── platform/ +│ └── Deploy-Platform-Foundation.ps1 +├── keyvault/ +│ └── Deploy-KeyVault.ps1 +└── networking/ + └── Deploy-VNet.ps1 +``` + +**Don't do this**: + +``` +# Inconsistent depth +deploy/ +├── Deploy-Platform-Foundation.ps1 # Root level +├── keyvault/ +│ └── core/ +│ └── foundation/ +│ └── Deploy-KeyVault.ps1 # 4 levels deep +└── networking/ + └── Deploy-VNet.ps1 # 2 levels +``` + +### 5. Include Tests Next to Modules + +**Do this**: + +``` +modules/ +└── AzureHelpers/ + ├── AzureHelpers.psm1 + ├── AzureHelpers.psd1 + ├── Public/ + ├── Private/ + ├── Tests/ + │ └── AzureHelpers.Tests.ps1 + └── README.md +``` + +**Don't do this**: + +``` +modules/ +└── AzureHelpers/ + ├── AzureHelpers.psm1 + └── AzureHelpers.psd1 + +tests/ # Tests separated from module +└── AzureHelpers.Tests.ps1 +``` + +### 6. Document Script Purpose in Filename + +**Do this**: + +``` +deploy/ +├── Deploy-KeyVault.ps1 # Clear purpose +├── Deploy-KeyVault-Monitoring.ps1 # Clear variant +└── Test-KeyVault-Access.ps1 # Clear test + +utilities/ +├── Get-EnvironmentInfo.ps1 # Clear retrieval +└── Export-Configuration.ps1 # Clear export +``` + +**Don't do this**: + +``` +deploy/ +├── KeyVault.ps1 # Unclear action +├── KV-Monitoring.ps1 # Unclear abbreviation +└── Access.ps1 # Too vague + +utilities/ +├── Environment.ps1 # What does it do? +└── Config.ps1 # Get? Set? Export? +``` + +### 7. Keep Modules Self-Contained + +**Do this**: + +``` +modules/ +└── LoggingModule/ + ├── LoggingModule.psm1 # Module file + ├── LoggingModule.psd1 # Manifest + ├── Public/ # Public functions + ├── Private/ # Private helpers + ├── Tests/ # Module tests + ├── README.md # Module docs + └── LICENSE.md # Module license (if needed) +``` + +**Don't do this**: + +``` +modules/ +├── LoggingModule.psm1 # Module at root +└── LoggingModule.psd1 + +helpers/ # Helper functions elsewhere +└── Logging-Helpers.ps1 + +tests/ # Tests separated +└── LoggingModule.Tests.ps1 +``` + +## Quality Checklist + +Before considering PowerShell organization complete: + +- [ ] **Folder structure created**: All standard folders exist (deploy/, utilities/, modules/, ci-cd/, tests/, docs/) +- [ ] **README.md files**: Every folder has README.md documenting purpose and contents +- [ ] **Naming consistency**: All scripts use PascalCase, all folders use lowercase-hyphen +- [ ] **Logical grouping**: Related scripts grouped in subfolders +- [ ] **No flat structures**: Scripts organized, not dumped in root folder +- [ ] **Module organization**: Modules self-contained with Public/, Private/, Tests/ +- [ ] **Test coverage**: Tests/ folder mirrors source structure +- [ ] **Documentation**: docs/ folder contains usage guides +- [ ] **CI/CD integration**: ci-cd/ folder has pipeline scripts +- [ ] **No orphans**: Every script has clear purpose and location +- [ ] **Cross-references**: README.md files link to related scripts +- [ ] **Standards alignment**: Follows Scripting Standards and Framework + +## Troubleshooting + +### Issue: Too Many Scripts in One Folder + +**Symptom**: 20+ scripts in single folder, difficult to find specific script + +**Solution**: Create logical subfolders by function: + +``` +Before: +utilities/ +├── Get-Script1.ps1 +├── Get-Script2.ps1 +... (20 more scripts) +└── Test-Script20.ps1 + +After: +utilities/ +├── monitoring/ +│ ├── Get-ResourceHealth.ps1 +│ └── Test-Connectivity.ps1 +├── reporting/ +│ ├── Get-CostReport.ps1 +│ └── Export-Configuration.ps1 +└── maintenance/ + ├── Cleanup-OldResources.ps1 + └── Update-Tags.ps1 +``` + +### Issue: Scripts Not Following Naming Convention + +**Symptom**: Mix of PascalCase, camelCase, lowercase, snake_case + +**Solution**: Standardize to PascalCase for all PowerShell scripts: + +```powershell +# Rename all scripts to PascalCase +Get-ChildItem -Path . -Filter "*.ps1" -Recurse | ForEach-Object { + $newName = ($_.BaseName -split '[_-]' | ForEach-Object { + $_.Substring(0,1).ToUpper() + $_.Substring(1).ToLower() + }) -join '-' + + $newName = $newName + ".ps1" + + if ($_.Name -ne $newName) { + Write-Host "Renaming: $($_.Name) -> $newName" + Rename-Item -Path $_.FullName -NewName $newName + } +} +``` + +### Issue: No Clear Separation Between Deploy and Utilities + +**Symptom**: Deployment scripts mixed with utility scripts + +**Solution**: Separate by purpose: + +- **deploy/**: Scripts that provision, deploy, or create resources +- **utilities/**: Scripts that query, test, or manage existing resources + +### Issue: Module Functions Scattered Across Scripts + +**Symptom**: Same functions duplicated in multiple scripts + +**Solution**: Extract common functions into modules: + +```powershell +# 1. Create module structure +New-Item -Path "modules/CommonHelpers" -ItemType Directory +New-Item -Path "modules/CommonHelpers/Public" -ItemType Directory +New-Item -Path "modules/CommonHelpers/Private" -ItemType Directory + +# 2. Extract function to Public/ folder +# Create Public/Get-AzureResource.ps1 with function + +# 3. Create module file that loads functions +# CommonHelpers.psm1 + +# 4. Import in scripts +Import-Module "$PSScriptRoot/../modules/CommonHelpers/CommonHelpers.psm1" +``` + +## Related Standards + +- **[Naming Conventions](../documentation/naming-conventions.mdx)** - Resource and file naming standards +- **[Scripting Standards](./scripting-standards.mdx)** - PowerShell coding standards +- **[Scripting Framework](./scripting-framework.mdx)** - Script templates and patterns +- **[Documentation Standards](../documentation/documentation-standards.mdx)** - Documentation formatting and structure + +## External Resources + +- [PowerShell Best Practices and Style Guide](https://poshcode.gitbook.io/powershell-practice-and-style/) +- [PowerShell Module Structure](https://docs.microsoft.com/en-us/powershell/scripting/developer/module/writing-a-windows-powershell-module) +- [Approved PowerShell Verbs](https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands) +- [Pester Testing Framework](https://pester.dev/) +- [Azure PowerShell Best Practices](https://docs.microsoft.com/en-us/powershell/azure/best-practices-scripts) + +--- + +**Version Control** + +- Created: 2025-12-10 by Product Technology Team +- Last Edited: 2026-02-08 by Product Technology Team +- Version: 1.1.0 +- Tags: powershell, organization, folder-structure, standards, best-practices +- Keywords: powershell, organization, folder-structure, modules, deployment, utilities, ci-cd, testing +- Author: Product Technology Team + diff --git a/standards/scripting/scripting-framework.mdx b/standards/scripting/scripting-framework.mdx new file mode 100644 index 000000000..3422df2f0 --- /dev/null +++ b/standards/scripting/scripting-framework.mdx @@ -0,0 +1,527 @@ +--- +id: scripting-framework +title: Scripting Framework +sidebar_label: Scripting Framework +--- +# PowerShell Scripting Framework Design + +[![Framework](https://img.shields.io/badge/Framework-Design-purple?logo=microsoftazure)](https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/) +[![PowerShell](https://img.shields.io/badge/PowerShell-Architecture-blue?logo=powershell)](https://docs.microsoft.com/en-us/powershell/) +[![Configuration](https://img.shields.io/badge/Configuration-Driven-green?logo=yaml)](https://yaml.org/) + +> **DOCUMENT CATEGORY**: Standard +> **SCOPE**: Scripting framework architecture for Azure Local infrastructure automation +> **PURPOSE**: Define design philosophy and patterns for environment-agnostic PowerShell scripts +> **MASTER REFERENCE**: [PowerShell Scripting Standards](./scripting-standards.mdx) + +--- + +## Overview + +This document describes the design philosophy and patterns for creating **environment-agnostic scripts** that work across all Azure Local environment repositories. The core principle is simple: **scripts read configuration from infrastructure.yml files using fixed variable paths**. + +## Core Concept + +Every environment has an `infrastructure.yml` file that follows the same structure. Scripts don't hardcode values - they read from configuration. The **variable paths never change**, only the **values differ** between environments. + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ SINGLE DEPLOYMENT SCENARIO │ +│ (One Customer, One Cluster, One Site) │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────────┐ │ +│ │ infrastructure.yml│ │ +│ │ (Customer Config)│ │ +│ └─────────┬─────────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────────────────────┐ │ +│ │ DEPLOYMENT SCRIPT │ │ +│ │ │ │ +│ │ Reads standard paths: │ │ +│ │ • azure_platform.tenant │ │ +│ │ • compute.azure_local │ │ +│ │ • identity.accounts │ │ +│ └──────────┬───────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────────────┐ │ +│ │ Azure Resources │ │ +│ │ Deployed │ │ +│ └──────────────────┘ │ +│ │ +│ Advanced: Multiple sites use same scripts, different config files│ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## The Variable Path Contract + +:::danger MANDATORY — DO NOT INVENT YAML PATHS +All variable paths used in scripts **must** come from the **infrastructure schema** and the **master-registry**. These two files are the single source of truth. If a path doesn't exist in the schema, it is not valid. +::: + +### Authoritative Sources + +Every `infrastructure.yml` file must conform to the schema. Every script must use paths defined in the schema. + +| Source | File | Purpose | +|--------|------|---------| +| **Infrastructure Schema** | [`configs/variables/assets/infrastructure.schema.json`](https://github.com/tierpoint/azl-env/schemas/infrastructure.schema.json) | Validates structure — defines every legal path, type, and constraint | +| **Master Registry** | `configs/variables/assets/master-registry.yaml` | Complete variable dictionary — descriptions, examples, metadata | +| **Registry Reference** | [Registry Reference](../variable-management/registry-reference.mdx) | Human-readable documentation | + +### Schema-Defined Sections (13-Section Hierarchy) + +The schema enforces this top-level structure — these are the **only** valid root keys: + +| Root Key | Required | Description | +|----------|----------|-------------| +| `_metadata` | No | File versioning and change tracking | +| `infrastructure_scenarios` | No | Deployment type taxonomy | +| `site` | **Yes** | Physical location, hardware, site codes | +| `environment` | **Yes** | Environment classification | +| `tags` | **Yes** | Azure resource tagging | +| `azure_platform` | **Yes** | Tenants, subscriptions, management groups, resource groups | +| `identity` | **Yes** | Accounts, Active Directory, managed identities, B2B, service principals | +| `networking` | **Yes** | Azure networking, on-prem networking, hybrid connectivity | +| `compute` | **Yes** | Clusters, cluster nodes, VMs, AVD, ARM deployment | +| `storage_accounts` | No | Azure Storage Accounts | +| `security` | No | Key Vault, Defender, Sentinel | +| `operations` | No | Monitoring, policy, patching, BCDR | +| `devops` | No | GitLab CI/CD, Terraform state | + +### How to Find the Right Path + +1. Open `infrastructure.schema.json` and search for the variable name +2. Follow the nesting: `root → section → subsection → property` +3. That nesting **is** the dotted path you use in PowerShell: `$cfg.root.section.subsection.property` +4. Cross-reference with `master-registry.yaml` for descriptions and examples +5. Comment the path in your script: `$val = $cfg.identity.active_directory.ntp_servers # identity.active_directory.ntp_servers` + +### Common Paths Quick Reference + +| Variable | Schema Path | PowerShell | +|----------|-------------|------------| +| Tenant ID | `azure_platform.tenant.id` | `$cfg.azure_platform.tenant.id` | +| Subscription | `azure_platform.subscriptions` | `$cfg.azure_platform.subscriptions` | +| Tags | `tags.*` | `$cfg.tags` | +| Site code | `site.code` | `$cfg.site.code` | +| Environment name | `environment.name` | `$cfg.environment.name` | +| Admin username | `identity.accounts.account_local_admin_username` | `$cfg.identity.accounts.account_local_admin_username` | +| Admin password | `identity.accounts.account_local_admin_password` | `$cfg.identity.accounts.account_local_admin_password` | +| AD domain | `identity.active_directory.ad_domain_fqdn` | `$cfg.identity.active_directory.ad_domain_fqdn` | +| NTP servers | `identity.active_directory.ntp_servers` | `@($cfg.identity.active_directory.ntp_servers)` | +| Gateway | `compute.azure_local.default_gateway` | `$cfg.compute.azure_local.default_gateway` | +| Subnet mask | `compute.azure_local.subnet_mask` | `$cfg.compute.azure_local.subnet_mask` | +| DNS servers | `compute.azure_local.dns_servers` | `@($cfg.compute.azure_local.dns_servers)` | +| Cluster nodes | `compute.cluster_nodes.` | `$cfg.compute.cluster_nodes.GetEnumerator()` | +| Node hostname | `compute.cluster_nodes..hostname` | `$_.Value.hostname` | +| Node IP | `compute.cluster_nodes..management_ip` | `$_.Value.management_ip` | + +> **Full reference**: [Registry Reference](../variable-management/registry-reference.mdx) — 970+ variables with descriptions, types, and examples. +> +> **Scripting rules**: [Scripting Standards — Variable Path Compliance](../scripting/scripting-standards.mdx#variable-path-compliance--master-registry--schema) + +--- + +## Key Vault Secret Pattern + +Secrets are **never stored in YAML**. Instead, configuration contains references using the `keyvault://` URI pattern: + +```yaml +credentials: + local_admin: + username: "osfadmin" + password: "keyvault://azl-platform-kv/local-admin-password" + + azure_local_sp: + app_id: "keyvault://azl-platform-kv/sp-azurelocal-appid" + secret: "keyvault://azl-platform-kv/sp-azurelocal-secret" +``` + +### How Scripts Resolve Secrets + +1. Script loads infrastructure.yml +2. Script reads the credential path (e.g., `$config.credentials.local_admin.password`) +3. Script detects `keyvault://` prefix +4. Script parses: `keyvault:///` +5. Script calls Azure Key Vault to retrieve actual value + +``` +┌─────────────────────────────────────────────────────────────┐ +│ SECRET RESOLUTION FLOW │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ infrastructure.yml │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ credentials: │ │ +│ │ local_admin: │ │ +│ │ password: "keyvault://azl-platform-kv/admin-pw" │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Script detects keyvault:// prefix │ │ +│ │ Parses: vault=azl-platform-kv, secret=admin-pw │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Azure Key Vault API │ │ +│ │ GET https://azl-platform-kv.vault.azure.net/... │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Actual password returned: "MyS3cur3P@ssw0rd!" │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Script Structure Pattern + +Every script should follow this pattern: + +```powershell +# 1. PARAMETER - Optional: Accept config path (defaults to infrastructure.yml) +param( + [string]$ConfigPath +) + +# 2. LOAD CONFIG - Read infrastructure.yml via config-loader +. "$PSScriptRoot/../utilities/helpers/config-loader.ps1" +$config = Get-InfrastructureConfig -ConfigPath $ConfigPath + +# 3. READ VALUES - Use fixed paths (these never change!) +$tenantId = $config.azure_platform.tenant.id # azure_platform.tenant.id +$subscriptionId = $config.azure_platform.subscriptions.lab.id # azure_platform.subscriptions..id +$kvName = $config.security.keyvault.kv_name # security.keyvault.kv_name +$tags = $config.tags # tags.* + +# 4. RESOLVE SECRETS - If needed (keyvault:// URI pattern) +$adminPassUri = $config.identity.accounts.account_local_admin_password # identity.accounts.account_local_admin_password +$password = Resolve-KeyVaultRef -KvUri $adminPassUri + +# 5. DO THE WORK - Same logic, different customer data +# ... your script logic here ... +``` + +--- + +## Example: Creating a Service Principal + +This example shows how a script reads from the customer's `infrastructure.yml`: + +```powershell +# New-DeploymentSP.ps1 +param( + [string]$ConfigPath +) + +# Load config via config-loader +. "$PSScriptRoot/../utilities/helpers/config-loader.ps1" +$config = Get-InfrastructureConfig -ConfigPath $ConfigPath + +# These paths are IDENTICAL across all deployments (v4.0.0 schema) +$tenantId = $config.azure_platform.tenant.id # azure_platform.tenant.id +$subscriptionId = $config.azure_platform.subscriptions.lab.id # azure_platform.subscriptions..id +$siteCode = $config.site.code # site.code +$kvName = $config.security.keyvault.kv_name # security.keyvault.kv_name + +# Create SP (same logic, different deployment data) +$spName = "sp-$siteCode-deploy" +$sp = New-AzADServicePrincipal -DisplayName $spName + +# Store credentials in Platform Key Vault +Set-AzKeyVaultSecret -VaultName $kvName -Name "$spName-appid" -SecretValue $sp.AppId +Set-AzKeyVaultSecret -VaultName $kvName -Name "$spName-secret" -SecretValue $sp.Secret +``` + +**Usage:** + +```powershell +# Deploy using default infrastructure.yml +.\New-DeploymentSP.ps1 + +# Or specify a different config path +.\New-DeploymentSP.ps1 -ConfigPath "./configs/alternate-infrastructure.yml" +``` + +--- + +## Dynamic Key Vault Lookup + +Scripts dynamically find the Key Vault from the customer's configuration: + +```powershell +# Script doesn't know which Key Vault - it reads from customer's config +$config = Get-Content "./configs/infrastructure.yml" -Raw | ConvertFrom-Yaml + +# This path is guaranteed to exist in every infrastructure.yml (v4.0.0 schema) +$kvName = $config.security.keyvault.kv_name # security.keyvault.kv_name + +# Now script can read/write secrets to the correct vault +$secret = Get-AzKeyVaultSecret -VaultName $kvName -Name "my-secret" +``` + +This means: + +- **Each customer** has their Key Vault name in `infrastructure.yml` +- **Scripts read the vault name** from config, not hardcoded +- **Same script works** across all customer deployments + +--- + +## Common Script Categories + +Scripts are organized by function in the `src/powershell/` directory with language variant subfolders: + +``` +src/powershell/ +├── discovery/ # Data collection from live environments +│ ├── Get-DellServerInventory.ps1 +│ ├── Get-AzureTenantInfo.ps1 +│ ├── Get-ClusterInventory.ps1 +│ └── Inventory-NetworkInfrastructure.ps1 +│ +├── generators/ # Documentation/config generation +│ ├── Generate-ClusterConfigMD.ps1 +│ ├── Generate-FromYAML.ps1 +│ ├── Generate-HostsFile.ps1 +│ └── Export-ToExcel.ps1 +│ +├── deploy/ # Deployment automation +│ ├── Deploy-AzureInfrastructure.ps1 +│ ├── Deploy-NetworkInfrastructure.ps1 +│ ├── Deploy-AzureLocalCluster.ps1 +│ └── Initialize-KeyVault.ps1 +│ +├── configure/ # Post-deployment configuration +│ ├── Configure-ActiveDirectory.ps1 +│ ├── Configure-DNSServers.ps1 +│ ├── Configure-StaticIP.ps1 +│ ├── Configure-WAC.ps1 +│ └── Register-AzureArc.ps1 +│ +├── utilities/ # Reusable functions/helpers +│ └── helpers/ +│ ├── config-loader.ps1 # Get-InfrastructureConfig function +│ ├── keyvault-helper.ps1 # Secret resolution functions +│ ├── logging.ps1 # Write-LogInfo, Write-LogError, etc. +│ └── validation.ps1 # Test-* validation functions +│ +├── ci-cd/ # Pipeline integration +│ ├── Build.ps1 +│ ├── Test.ps1 +│ └── Deploy-Pipeline.ps1 +│ +└── tests/ # Pester tests + ├── unit/ + └── integration/ +``` + +### Customer Deployment Example + +This shows how scripts read configuration from the customer's `infrastructure.yml`: + +```powershell +# Load configuration +. "$PSScriptRoot/../utilities/helpers/config-loader.ps1" +$config = Get-InfrastructureConfig + +# Read values from YAML (same paths across all deployments) +$clusterName = $config.compute.azure_local.cluster_name # compute.azure_local.cluster_name +$nodeCount = $config.compute.azure_local.cluster_node_count # compute.azure_local.cluster_node_count +$topology = $config.compute.azure_local.network_topology # compute.azure_local.network_topology +$location = $config.azure_platform.region # azure_platform.region + +# Script logic adapts based on customer's config values +Write-Host "Deploying cluster: $clusterName" +Write-Host "Location: $location" +Write-Host "Nodes: $nodeCount" + +if ($topology -eq "switchless") { + Write-Host "Configuring direct RDMA connections (switchless topology)" +} +elseif ($topology -eq "switched") { + Write-Host "Configuring switched network topology" +} +``` + +**Key Point**: One deployment, one `infrastructure.yml`, one configuration. The scripts adapt to whatever values are in the file. + +--- + +## Language Variants + +Each functional folder can contain language-specific subfolders when needed: + +| Subfolder | Contents | File Extensions | +|-----------|----------|-----------------| +| `powershell/` | PowerShell + Az modules (primary) | `.ps1` | +| `azurecli/` | Azure CLI (PowerShell or Bash) | `.ps1`, `.sh` | + +**PowerShell + Az Modules** (Primary): + +```powershell +# Uses Azure PowerShell modules +Connect-AzAccount +New-AzResourceGroup -Name $rgName -Location "eastus" +``` + +**Azure CLI (PowerShell wrapper)**: + +```powershell +# Uses az CLI commands from PowerShell +az login +az group create --name $rgName --location "eastus" +``` + +**Azure CLI (Bash)**: + +```bash +#!/bin/bash +# Uses az CLI commands from Bash +az login +az group create --name "$RG_NAME" --location "eastus" +``` + +--- + +## Benefits of This Approach + +| Benefit | Description | +|---------|-------------| +| **Write Once, Use Everywhere** | One script works for any deployment | +| **No Hardcoded Values** | Everything comes from configuration | +| **Secrets Stay Secure** | Never in source control, always in Key Vault | +| **Predictable** | Same paths, same behavior, different data | +| **Maintainable** | Fix a bug once, all environments benefit | +| **Auditable** | Configuration is version controlled | +| **Type-Safe** | JSON Schema validates infrastructure.yml structure | +| **Environment-Aware** | Scripts adapt logic based on config values (switchless, nested, GPU, etc.) | + +--- + +## Rules for Script Authors + +1. **NEVER hardcode** Azure IDs, names, passwords, or environment-specific values +2. **ALWAYS** accept `-ConfigPath` parameter with a sensible default path +3. **ALWAYS** use `Get-InfrastructureConfig` to load configuration +4. **ALWAYS** use the documented variable paths +5. **ALWAYS** use `keyvault://` URIs for secrets +6. **NEVER** store secrets in YAML files +7. **TEST** your script with different configurations to verify portability +8. **USE** the platform Key Vault for shared secrets across deployments +9. **DOCUMENT** which config paths your script requires +10. **LOG** all operations using standard logging functions + +--- + +## Summary + +The pattern is simple: + +| Aspect | Rule | +|--------|------| +| **Configuration** | Lives in `infrastructure.yml` at repo root | +| **Variable Paths** | Fixed across all deployments (see contract above) | +| **Secrets** | Referenced via `keyvault://` URI pattern | +| **Scripts** | Accept `-ConfigPath`, load config, use fixed paths | +| **Key Vault Lookup** | Dynamic from `security.keyvault.kv_name` | +| **Platform Key Vault** | Shared secrets referenced via config | + +**One script. Any deployment. Same paths. Different values.** + +--- + +## Real-World Example: Deploy Azure Resource Groups + +This script works for **any deployment** without modification: + +```powershell +<# +.SYNOPSIS + Deploy Azure resource groups for a deployment. + +.PARAMETER ConfigPath + Path to the infrastructure.yml configuration file. +#> + +[CmdletBinding(SupportsShouldProcess)] +param( + [Parameter(Mandatory = $false)] + [ValidateScript({Test-Path $_})] + [string]$ConfigPath = "configs/infrastructure.yml" +) + +# Load config +. "$PSScriptRoot/../utilities/helpers/config-loader.ps1" +. "$PSScriptRoot/../utilities/helpers/logging.ps1" + +$config = Get-InfrastructureConfig -ConfigPath $ConfigPath + +# Read values from config (same paths for all environments) +$subscriptionId = $config.azure_platform.subscriptions.lab.id # azure_platform.subscriptions..id +$location = $config.azure_platform.region # azure_platform.region +$tags = $config.tags # tags.* +$resourceGroups = $config.azure_platform.resource_groups # azure_platform.resource_groups + +# Set Azure context +Set-AzContext -SubscriptionId $subscriptionId | Out-Null +Write-LogInfo "Deploying resource groups for $Environment to $location" + +# Deploy each resource group (same logic, different data) +foreach ($rgKey in $resourceGroups.PSObject.Properties.Name) { + $rgName = $resourceGroups.$rgKey.name + + if ($PSCmdlet.ShouldProcess($rgName, "Create Resource Group")) { + $existing = Get-AzResourceGroup -Name $rgName -ErrorAction SilentlyContinue + if ($existing) { + Write-LogWarning "Resource group exists: $rgName" + } + else { + New-AzResourceGroup -Name $rgName -Location $location -Tag $tags + Write-LogSuccess "Created resource group: $rgName" + } + } +} +``` + +**Usage:** + +```powershell +# Deploy using default infrastructure.yml +.\Deploy-ResourceGroups.ps1 -WhatIf +.\Deploy-ResourceGroups.ps1 + +# Or specify a different config file +.\Deploy-ResourceGroups.ps1 -ConfigPath "./alternate-config.yml" +``` + +Same script. Different deployments. Same outcome. **That's the power of configuration-driven automation.** + +--- + +## Related Documentation + +- [PowerShell Scripting Standards](./scripting-standards.mdx) - Coding standards and quality requirements +- [PowerShell Organization Standard](./powershell-organization-standard.mdx) - Script organization in repositories +- [Naming Conventions](../documentation/naming-conventions.mdx) - File and resource naming standards + +--- + +**Version Control** + +- Created: 2025-12-10 by Product Technology Team +- Last Edited: 2025-12-10 by GitHub Copilot +- Version: 1.0.0 +- Tags: standards, powershell, framework, architecture, configuration +- Keywords: powershell, scripting, framework, configuration-driven, environment-agnostic, azure-local +- Author: Product Technology Team + diff --git a/standards/scripting/scripting-standards.mdx b/standards/scripting/scripting-standards.mdx new file mode 100644 index 000000000..dfc307c68 --- /dev/null +++ b/standards/scripting/scripting-standards.mdx @@ -0,0 +1,1228 @@ +--- +id: scripting-standards +title: Scripting Standards +sidebar_label: Scripting Standards +--- +# PowerShell Scripting Standards + +[![Standards](https://img.shields.io/badge/Standards-PowerShell-purple?logo=powershell)](https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/required-development-guidelines) +[![Automation](https://img.shields.io/badge/Automation-Scripts-blue?logo=azurepipelines)](https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/) +[![Best-Practices](https://img.shields.io/badge/Best-Practices-green?logo=microsoftazure)](https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/strongly-encouraged-development-guidelines) + +> **DOCUMENT CATEGORY**: Standard +> **SCOPE**: PowerShell scripting standards for Azure Local infrastructure automation +> **PURPOSE**: Define coding standards for all PowerShell scripts across environment repositories +> **MASTER REFERENCE**: [PowerShell Organization Standard](./powershell-organization-standard.mdx) + +--- + + +## Script Naming and Execution Standards + +### Naming Conventions + +| Script Type | Naming Pattern | Example | +|----------------------------|---------------------------------------|---------------------------------| +| PowerShell Core | Verb-Noun.ps1 | New-Resource.ps1 | +| Azure PowerShell | Verb-AzResource.ps1 | New-AzKeyVault.ps1 | +| Azure CLI for PowerShell | az-verb-resource.ps1 | az-create-vnet.ps1 | +| Azure CLI (Bash) | az-verb-resource.sh | az-create-vnet.sh | +| Standalone (no config) | Verb-Noun-Standalone.ps1 | New-AzKeyVault-Standalone.ps1 | + +Scripts intended for remote/management-box execution must start with the `Invoke-` prefix (e.g., Invoke-DeployCluster.ps1). + +### Script Options in Documentation + +Provisioning documentation provides multiple script options for each step: + +| Option | Type | Description | +|--------|------|-------------| +| Option 1 | Manual | Step-by-step portal/UI instructions | +| Option 2 | Azure PowerShell | Config-driven script using infrastructure.yml | +| Option 3 | Azure CLI (PowerShell) | Config-driven az CLI in PowerShell | +| Option 4 | Azure CLI (Bash) | Config-driven az CLI in Bash | +| Option 5 | Standalone | Self-contained script with inline variables (no external dependencies) | + +### Script Structure and Config Usage + +**Options 2-4 (Config-Driven):** +- All scripts must use variables/config from infrastructure.yml (never hardcoded values) +- All scripts must accept a config/environment parameter +- All scripts must use helpers (config-loader, keyvault-helper, logging, etc.) +- **All YAML variable paths used in scripts must match the master-registry and infrastructure schema** — see [Variable Path Compliance](#variable-path-compliance) below + +**`Invoke-` Scripts (Remotely-Executed Orchestration Scripts):** + +All scripts that execute operations remotely via PSRemoting **must** implement the following credential resolution order — no exceptions: + +1. **`-Credential` parameter** — If passed directly at the command line, use it immediately (skip all other resolution) +2. **Key Vault** — Read the appropriate account credentials from `identity.accounts` in `infrastructure.yml` (see account selection rule below). Attempt `Az.KeyVault` module first, fall back to `az` CLI +3. **Interactive prompt** — If Key Vault is unavailable or returns null, fall back to `Get-Credential` with the username pre-filled + +The `Resolve-KeyVaultRef` function (see [Key Vault Operations](#key-vault-operations)) implements steps 2 and 3. Copy it verbatim into every `Invoke-` script — do not create a new variant. + +#### PSRemoting Account Selection Rule + +Which account to use for PSRemoting depends on whether the target nodes are domain-joined: + +| Deployment Phase | Node State | Account to Use | Config Paths | +|-----------------|------------|---------------|-------------| +| Stage `04-cluster-deployment`, **phase 01–05** (up to and including cluster deployment) | Not yet domain-joined — local accounts only | Local Administrator | `identity.accounts.account_local_admin_username` / `account_local_admin_password` | +| Stage `04-cluster-deployment`, **phase 06+** (post-deployment) and **all later stages** | Domain-joined — Kerberos auth available | LCM domain account | `identity.accounts.account_lcm_username` / `account_lcm_password` | + +:::warning Use the LCM account after cluster deployment +Once the cluster has been deployed (stage 04 / phase 05), all nodes are domain-joined. PSRemoting from a management machine must use the **LCM domain account** (`identity.accounts.account_lcm_username`) — not the local Administrator — to authenticate via Kerberos. Using the local admin account against a domain-joined node from a non-domain machine will fail unless TrustedHosts is configured, which is **not permitted** in these scripts. +::: + +#### Required Parameters for All `Invoke-` Scripts + +Every `Invoke-` script **must** expose the following parameters — no exceptions: + +| Parameter | Type | Default | Purpose | +|-----------|------|---------|---------| +| `-ConfigPath` | `[string]` | `""` | Path to `infrastructure.yml`. Auto-discovered if not provided. | +| `-Credential` | `[PSCredential]` | `$null` | Override credential resolution (Key Vault / prompt). | +| `-TargetNode` | `[string[]]` | `@()` (all nodes) | Limit execution to one or more named nodes. Matches against `hostname` or `nodename` from `infrastructure.yml`. Empty = run all nodes. | +| `-WhatIf` | `[switch]` | `$false` | Dry-run mode — log what *would* happen without making any changes. See [Execution Modes](#invoke-script-execution-modes). | +| `-LogPath` | `[string]` | `""` (auto) | Override log file path. Default: `./logs//.log` relative to the current working directory (repo root). | + +Additionally, all `Invoke-` scripts **must** use `[CmdletBinding()]` to enable the built-in `-Verbose` and `-Debug` switches. + +```powershell +# Example: run against a single node +.\Invoke-ConfigureNTP-Orchestrated.ps1 -ConfigPath .\configs\infrastructure.yml -TargetNode node02 + +# Example: dry-run to see what would change +.\Invoke-ConfigureNTP-Orchestrated.ps1 -ConfigPath .\configs\infrastructure.yml -WhatIf + +# Example: verbose output for troubleshooting +.\Invoke-ConfigureNTP-Orchestrated.ps1 -ConfigPath .\configs\infrastructure.yml -TargetNode node01 -Verbose + +# Example: debug-level output +.\Invoke-ConfigureNTP-Orchestrated.ps1 -ConfigPath .\configs\infrastructure.yml -Debug + +# Example: custom log path +.\Invoke-ConfigureNTP-Orchestrated.ps1 -ConfigPath .\configs\infrastructure.yml -LogPath C:\logs\ntp-run.log +``` + +#### YAML-Overridable Parameters + +All values that an `Invoke-` script reads from `infrastructure.yml` **must** also be exposed as optional parameters so they can be overridden at the command line without editing the config file. When a parameter is supplied, it takes precedence over the YAML value. When omitted, the YAML value is used. + +**Required pattern:** + +```powershell +param( + [string]$ConfigPath = "", + [PSCredential]$Credential, + [string[]]$TargetNode = @(), + + # YAML-overridable values — empty string / empty array = use YAML + [string] $NTPServers = "", # identity.active_directory.ntp_servers + [string] $Gateway = "", # compute.azure_local.default_gateway + [string] $SubnetMask = "", # compute.azure_local.subnet_mask + [string[]]$DnsServers = @(), # compute.azure_local.dns_servers +) + +# After loading YAML, apply overrides: +if ($NTPServers -ne "") { $ntpPeer = $NTPServers } +if ($Gateway -ne "") { $gateway = $Gateway } +if ($SubnetMask -ne "") { $subnetMask = $SubnetMask } +if ($DnsServers.Count -gt 0) { $dnsServers = $DnsServers } +``` + +**Rules:** +- Parameter names must match the semantic purpose (e.g., `-NTPServers`, not `-Param1`) +- Comment each parameter with its corresponding `infrastructure.yml` path +- Never silently ignore a supplied override — log it at `[WARN]` level so it's visible in output +- Do not make YAML-override parameters mandatory — they must always have a no-op default (empty string or empty array) + +**Option 5 (Standalone):** +- All variables defined in a `#region CONFIGURATION` block at the top +- No external dependencies (no config-loader, no infrastructure.yml, no helpers) +- Variable naming matches infrastructure.yml paths (e.g., `$azure_tenant_id`) +- Can be copied, shared, and run anywhere without the toolkit +- Use for: external sharing, demos, single-use deployments +- Do NOT use for: multi-environment automation, CI/CD pipelines + +### Variable Path Compliance — Master Registry & Schema + +:::danger MANDATORY +Every variable path used in any script **must** come from the master-registry and conform to the infrastructure schema. **Do not invent, guess, or approximate YAML paths.** This is the #1 source of script failures. +::: + +**Authoritative sources (in order of precedence):** + +| Source | Location | Purpose | +|--------|----------|--------| +| **Infrastructure Schema** | `configs/variables/assets/infrastructure.schema.json` | JSON Schema that validates every `infrastructure.yml` — defines the exact structure, required fields, types, and patterns | +| **Master Registry** | `configs/variables/assets/master-registry.yaml` | Complete variable registry with 970+ variables, descriptions, examples, and metadata | +| **Registry Reference Docs** | [Registry Reference](../variable-management/registry-reference.mdx) | Human-readable documentation of all variable paths | + +**Rules — no exceptions:** + +1. **Look up before you code** — Before writing any `$cfg.some.path` in a script, verify the exact path exists in `infrastructure.schema.json`. If it's not in the schema, it's not a valid path. +2. **Schema is the contract** — The schema defines what `infrastructure.yml` files must contain. Scripts must use paths that exist in the schema. If a path doesn't exist in the schema, the script is wrong — not the schema. +3. **Master registry is the dictionary** — The master-registry provides descriptions, examples, and metadata for every variable. Use it to understand what a variable means and how it should be used. +4. **Values live in `infrastructure.yml`** — The schema defines the structure, the master-registry defines the vocabulary, and `infrastructure.yml` contains the actual customer values. Scripts read values from `infrastructure.yml` using paths defined by the schema. +5. **Comment every path** — Every YAML path access in a script must have an inline comment showing the full dotted path: + +```powershell +# CORRECT — path verified against infrastructure.schema.json +$gateway = $cfg.compute.azure_local.default_gateway # compute.azure_local.default_gateway +$subnetMask = $cfg.compute.azure_local.subnet_mask # compute.azure_local.subnet_mask +$dnsServers = @($cfg.compute.azure_local.dns_servers) # compute.azure_local.dns_servers +$ntpServers = @($cfg.identity.active_directory.ntp_servers) # identity.active_directory.ntp_servers +$adminUser = $cfg.identity.accounts.account_local_admin_username # identity.accounts.account_local_admin_username +$adminPass = $cfg.identity.accounts.account_local_admin_password # identity.accounts.account_local_admin_password + +# WRONG — invented paths that don't exist in the schema +$gateway = $cfg.network.management.gateway # ❌ DOES NOT EXIST +$ntpServer = $cfg.ntp.server # ❌ DOES NOT EXIST +$dnsServers = @($cfg.dns.primary, $cfg.dns.secondary) # ❌ DOES NOT EXIST +``` + +6. **Schema validation is mandatory before deployment** — Run `Validate-Infrastructure.ps1` against every `infrastructure.yml` before any script execution. See [Schema Validation](../variable-management/schema-validation.mdx). + +**Quick reference — common Phase 03 paths:** + +| Variable | Schema Path | PowerShell Access | +|----------|-------------|------------------| +| Gateway | `compute.azure_local.default_gateway` | `$cfg.compute.azure_local.default_gateway` | +| Subnet mask | `compute.azure_local.subnet_mask` | `$cfg.compute.azure_local.subnet_mask` | +| DNS servers | `compute.azure_local.dns_servers` | `@($cfg.compute.azure_local.dns_servers)` | +| NTP servers | `identity.active_directory.ntp_servers` | `@($cfg.identity.active_directory.ntp_servers)` | +| Cluster nodes | `compute.cluster_nodes` | `$cfg.compute.cluster_nodes.GetEnumerator()` | +| Node hostname | `compute.cluster_nodes..hostname` | `$_.Value.hostname` | +| Node IP | `compute.cluster_nodes..management_ip` | `$_.Value.management_ip` | +| Admin username | `identity.accounts.account_local_admin_username` | `$cfg.identity.accounts.account_local_admin_username` | +| Admin password | `identity.accounts.account_local_admin_password` | `$cfg.identity.accounts.account_local_admin_password` | + +> See [Variable Management Standard](../variable-management/index.mdx) and [Registry Reference](../variable-management/registry-reference.mdx) for the complete list. + +### Linting and Testing + +- All PowerShell scripts must pass PSScriptAnalyzer (linting and syntax) +- All Bash scripts must pass ShellCheck +- CI/pre-commit hooks will enforce naming, config usage, and structure + +### Onboarding and Quickstart + +See the [Onboarding](#onboarding) section below for step-by-step setup, template use, and testing instructions. A quickstart script is provided to install required tools and validate your environment. + +All standards, onboarding, and automation tools are versioned and cross-linked for discoverability. + +**Related Documentation:** +- [Key Inputs and Variables](../../product-azure-local-anywhere/implementation/03-key-inputs-and-variables.mdx) - infrastructure.yml structure and usage +- [How to Use This Runbook](../../product-azure-local-anywhere/implementation/02-how-to-use-this-runbook.mdx) - Script execution patterns +- [Scripting Framework](./scripting-framework.mdx) - Architectural patterns and templates + +--- +## Overview + +This document defines the coding standards for all PowerShell scripts across Product Technology Azure Local environment repositories. Following these standards ensures scripts work consistently across all environments and maintain high quality, readability, and reliability. + +## Golden Rule + +> **Scripts read configuration from `infrastructure.yml` using fixed variable paths. The paths never change - only the values differ between environments.** + +This principle ensures: + +- **Portability**: Same script works across all customer deployment sites +- **Maintainability**: Update script once, deploy to all environments +- **Consistency**: Predictable behavior across all repos +- **Version Control**: Track configuration changes separately from logic + +--- + +## Required Structure for All Scripts + +Every PowerShell script must follow this template: + +```powershell +#Requires -Version 7.0 + +<# +.SYNOPSIS + Brief description of what the script does. + +.DESCRIPTION + Detailed description including: + - What configuration it reads from infrastructure.yml + - What Azure resources it creates/modifies + - What secrets it reads/writes to Key Vault + +.PARAMETER ConfigPath + Path to the infrastructure.yml configuration file for the target deployment. + Defaults to configs/infrastructure.yml in the repository root. + +.EXAMPLE + .\New-AzureLocalSP.ps1 -ConfigPath "configs/infrastructure.yml" + + Creates a service principal using the specified configuration. + +.EXAMPLE + .\New-AzureLocalSP.ps1 -ConfigPath "configs/infrastructure.yml" -WhatIf + + Shows what would be created without making changes. + +.NOTES + Requires: Az module, powershell-yaml module + Author: Product Technology Team + Date: 2025-12-10 + Version: 1.0.0 +#> + +[CmdletBinding(SupportsShouldProcess)] +param( + [Parameter(Mandatory = $false)] + [ValidateScript({Test-Path $_})] + [string]$ConfigPath = "configs/infrastructure.yml", + + [Parameter(Mandatory = $false)] + [switch]$WhatIf +) + +#Requires -Modules Az.Accounts, Az.KeyVault, powershell-yaml + +# ============================================================================ +# INITIALIZATION +# ============================================================================ +$ErrorActionPreference = "Stop" +$scriptRoot = $PSScriptRoot + +# Import utilities +. "$scriptRoot/../utilities/helpers/config-loader.ps1" +. "$scriptRoot/../utilities/helpers/keyvault-helper.ps1" +. "$scriptRoot/../utilities/helpers/logging.ps1" + +# Load environment configuration +$config = Get-InfrastructureConfig -ConfigPath $ConfigPath + +# ============================================================================ +# MAIN LOGIC +# ============================================================================ + +# Your script logic here, using $config to access all values + +# ============================================================================ +# CLEANUP / OUTPUT +# ============================================================================ +``` + +### Key Components + +| Component | Purpose | Required | +|-----------|---------|----------| +| **PS Version Requirement** | `#Requires -Version 7.0` — must be the first line of every script | Yes | +| **Comment-Based Help** | `.SYNOPSIS`, `.DESCRIPTION`, `.PARAMETER`, `.EXAMPLE`, `.NOTES` | Yes | +| **CmdletBinding** | Enable advanced function features | Yes | +| **Parameter Validation** | `ValidateScript` for ConfigPath parameter | Yes | +| **Requires Statement** | Declare module dependencies | Yes | +| **Error Handling** | `$ErrorActionPreference = "Stop"` | Yes | +| **Relative Paths** | Use `$PSScriptRoot` for portability | Yes | +| **Logging Functions** | Use standard logging helpers | Yes | + +--- + +## Variable Path Contract + +:::warning Canonical Reference +This section provides a quick-reference subset. The **complete** path contract is defined in the [Variable Path Compliance](#variable-path-compliance--master-registry--schema) section above and the [Scripting Framework — Variable Path Contract](./scripting-framework.mdx#the-variable-path-contract). Always verify paths against `infrastructure.schema.json`. +::: + +These paths are **guaranteed to exist** in every `infrastructure.yml` (v4.0.0 schema). Scripts MUST use these paths: + +### Core Identifiers + +| Path | Type | Description | +|------|------|-------------| +| `site.code` | string | Site code (short identifier for the deployment) | +| `site.name` | string | Site display name | +| `environment` | string | Environment identifier (may be a string or object depending on deployment) | +| `tags.Environment` | string | Environment tag | +| `tags.Project` | string | Project name | +| `tags.ManagedBy` | string | Managing entity (e.g., "Infrastructure as Code") | +| `tags.Owner` | string | Owner | + +### Azure Platform + +| Path | Type | Description | +|------|------|-------------| +| `azure_platform.tenant.id` | guid | Azure tenant ID | +| `azure_platform.tenant.name` | string | Tenant display name | +| `azure_platform.region` | string | Default Azure region (e.g., "eastus") | +| `azure_platform.subscriptions..id` | guid | Subscription ID | +| `azure_platform.subscriptions..name` | string | Subscription name | +| `azure_platform.subscriptions..purpose` | string | Subscription purpose | + +### Identity & Credentials + +| Path | Type | Description | +|------|------|-------------| +| `identity.accounts.account_local_admin_username` | string | Local admin username (used before nodes are domain-joined) | +| `identity.accounts.account_local_admin_password` | string | Local admin password (`keyvault://` URI) | +| `identity.accounts.account_lcm_username` | string | LCM domain account username (used after nodes are domain-joined — phase 06+) | +| `identity.accounts.account_lcm_password` | string | LCM domain account password (`keyvault://` URI) | +| `identity.active_directory.ntp_servers` | array | NTP server addresses | +| `identity.active_directory.ad_domain_fqdn` | string | AD domain FQDN | + +### Compute + +| Path | Type | Description | +|------|------|-------------| +| `compute.azure_local.cluster_name` | string | Cluster name | +| `compute.azure_local.cluster_node_count` | integer | Number of nodes | +| `compute.azure_local.default_gateway` | string | Default gateway IP | +| `compute.azure_local.subnet_mask` | string | Subnet mask | +| `compute.azure_local.dns_servers` | array | DNS server addresses | +| `compute.cluster_nodes..hostname` | string | Node hostname | +| `compute.cluster_nodes..management_ip` | string | Node management IP | + +### Security + +| Path | Type | Description | Example | +|------|------|-------------|---------| +| `security.keyvault.kv_name` | string | Platform Key Vault name | `kv-tplabs-platform` | +| `security.credentials.local_admin_password` | string | Local admin password (`keyvault://` URI) | `keyvault://kv-tplabs-platform/local-admin-password` | + +--- + +## Key Vault Operations + +### Reading Secrets + +**Method 1: Direct Key Vault access using config values** + +```powershell +# Load environment config +$config = Get-InfrastructureConfig -ConfigPath $ConfigPath + +# Get Key Vault name from config # security.keyvault.kv_name +$kvName = $config.security.keyvault.kv_name # e.g., "kv-tplabs-platform" + +# Get admin password URI (keyvault:// reference) # identity.accounts.account_local_admin_password +$adminPassUri = $config.identity.accounts.account_local_admin_password + +# Resolve via Resolve-KeyVaultRef (see Credential Resolution below) +$adminPass = Resolve-KeyVaultRef -KvUri $adminPassUri +``` + +**Method 2: Direct Az module access** + +```powershell +# Read KV name from config # security.keyvault.kv_name +$kvName = $config.security.keyvault.kv_name + +# Retrieve a specific secret by name +$secret = Get-AzKeyVaultSecret -VaultName $kvName -Name "local-admin-password" -AsPlainText +``` + +**Method 3: Platform Key Vault for shared secrets** + +```powershell +# Shared secrets stored in platform KV (e.g., GitHub tokens, VPN keys) +$kvName = $config.security.keyvault.kv_name # security.keyvault.kv_name +$githubToken = Get-AzKeyVaultSecret -VaultName $kvName -Name "api-github-pat" -AsPlainText +``` + +### Writing Secrets + +```powershell +# Write to the platform Key Vault specified in config +$kvName = $config.security.keyvault.kv_name # security.keyvault.kv_name + +Set-AzKeyVaultSecret -VaultName $kvName ` + -Name "new-secret-name" ` + -SecretValue $secureString ` + -ContentType "password" +``` + +### Parsing keyvault:// URIs + +Some config values use the `keyvault://` URI pattern: + +```yaml +credentials: + vpn: + shared_key_secret: "keyvault://azl-platform-kv/vpn-shared-key" +``` + +Parse and retrieve: + +```powershell +$uri = $config.credentials.vpn.shared_key_secret +$secret = Get-SecretFromUri -Uri $uri +``` + +### Credential Resolution for Invoke- Scripts + +All `Invoke-` (orchestrated/remote) scripts **must** use this exact `Resolve-KeyVaultRef` function and credential resolution sequence. Copy it verbatim — do not create variants. + +```powershell +function Resolve-KeyVaultRef { + param([string]$KvUri) + if ($KvUri -notmatch '^keyvault://([^/]+)/(.+)$') { Write-Log " Not a Key Vault URI: $KvUri" "WARN"; return $null } + $vaultName = $Matches[1] + $secretName = $Matches[2] + + if (Get-Module -Name Az.KeyVault -ListAvailable -ErrorAction SilentlyContinue) { + try { + Write-Log " Retrieving '$secretName' from '$vaultName' (Az.KeyVault)..." + $secret = Get-AzKeyVaultSecret -VaultName $vaultName -Name $secretName -AsPlainText -ErrorAction Stop + if ($secret) { Write-Log " Secret retrieved." "PASS"; return $secret } + Write-Log " Az.KeyVault returned no secret." "WARN" + } catch { Write-Log " Az.KeyVault failed: $_" "WARN" } + Write-Log " Falling back to Azure CLI..." "WARN" + } else { + Write-Log " Az.KeyVault module not found — trying Azure CLI..." "WARN" + } + + try { + $azCmd = Get-Command az -ErrorAction SilentlyContinue + if (-not $azCmd) { Write-Log " Azure CLI (az) not found." "WARN"; return $null } + Write-Log " Retrieving '$secretName' from '$vaultName' (az CLI)..." + $tmpErr = [System.IO.Path]::GetTempFileName() + $val = (& az keyvault secret show --vault-name $vaultName --name $secretName --query value --output tsv --only-show-errors 2>$tmpErr) + $azErr = (Get-Content $tmpErr -Raw -ErrorAction SilentlyContinue).Trim() + Remove-Item $tmpErr -ErrorAction SilentlyContinue + if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($val)) { + $errDetail = if ($azErr) { ": $azErr" } else { " (exit $LASTEXITCODE)" } + Write-Log " az CLI failed$errDetail." "WARN" + return $null + } + Write-Log " Secret retrieved (az CLI)." "PASS" + return $val + } catch { Write-Log " az CLI failed: $_" "WARN"; return $null } +} +``` + +**Credential resolution block — two variants depending on deployment phase.** + +Use the correct variant based on the [PSRemoting Account Selection Rule](#psremoting-account-selection-rule) above. + +**Variant A — Pre-domain (stage 04 phases 01–05, nodes not yet domain-joined):** + +```powershell +# CREDENTIAL RESOLUTION ORDER: +# 1. -Credential parameter (passed directly) +# 2. Key Vault — local Administrator account (nodes not yet domain-joined) +# 3. Interactive Get-Credential prompt +if (-not $Credential) { + $adminUser = $cfg.identity.accounts.account_local_admin_username # identity.accounts.account_local_admin_username + $adminPassUri = $cfg.identity.accounts.account_local_admin_password # identity.accounts.account_local_admin_password + Write-Log "Resolving local admin credentials from Key Vault..." + $adminPass = Resolve-KeyVaultRef -KvUri $adminPassUri + if ($adminPass) { + $Credential = New-Object PSCredential( + $adminUser, + (ConvertTo-SecureString $adminPass -AsPlainText -Force) + ) + Write-Log "Credentials resolved for '$adminUser'." "PASS" + } else { + Write-Log "Key Vault unavailable — prompting for credentials." "WARN" + $Credential = Get-Credential -Message "Enter local Administrator credentials" -UserName $adminUser + } +} +``` + +**Variant B — Post-domain (stage 04 phase 06+ and all later stages, nodes are domain-joined):** + +```powershell +# CREDENTIAL RESOLUTION ORDER: +# 1. -Credential parameter (passed directly) +# 2. Key Vault — LCM domain account (nodes are domain-joined; Kerberos auth required) +# 3. Interactive Get-Credential prompt +if (-not $Credential) { + $lcmUser = $cfg.identity.accounts.account_lcm_username # identity.accounts.account_lcm_username + $lcmPassUri = $cfg.identity.accounts.account_lcm_password # identity.accounts.account_lcm_password + Write-Log "Resolving LCM credentials from Key Vault..." + $lcmPass = Resolve-KeyVaultRef -KvUri $lcmPassUri + if ($lcmPass) { + $Credential = New-Object PSCredential( + $lcmUser, + (ConvertTo-SecureString $lcmPass -AsPlainText -Force) + ) + Write-Log "Credentials resolved for '$lcmUser'." "PASS" + } else { + Write-Log "Key Vault unavailable — prompting for credentials." "WARN" + $Credential = Get-Credential -Message "Enter LCM credentials for PSRemoting to cluster nodes" -UserName $lcmUser + } +} +``` + +:::info Why not TrustedHosts? +Do **not** add nodes to `WSMan:\localhost\Client\TrustedHosts` as a workaround. Once nodes are domain-joined, PSRemoting uses Kerberos — no TrustedHosts entry needed. Adding workstation entries to TrustedHosts bypasses Kerberos and is not permitted in these scripts. +::: + +--- + +## Service Principal Patterns + +### Creating a Service Principal + +```powershell +# 1. Read required values from config +$tenantId = $config.azure_platform.tenant.id # azure_platform.tenant.id +$subscriptionId = $config.azure_platform.subscriptions.lab.id # azure_platform.subscriptions..id +$spName = "sp-$($config.site.code)-deploy" # site.code + +# 2. Create the SP +$sp = New-AzADServicePrincipal -DisplayName $spName + +# 3. Store the secret in Platform Key Vault +$kvName = $config.security.keyvault.kv_name # security.keyvault.kv_name +Set-AzKeyVaultSecret -VaultName $kvName -Name $spName -SecretValue $sp.PasswordCredentials.SecretText + +# 4. Output what was created (for updating infrastructure.yml) +Write-Output "Created SP: $spName" +Write-Output "App ID: $($sp.AppId)" +Write-Output "Object ID: $($sp.Id)" +Write-Output "Secret stored in: $kvName/$spName" +``` + +--- + +## Logging Standards + +Use the logging utilities for consistent output: + +```powershell +. "$scriptRoot/../utilities/helpers/logging.ps1" + +Write-LogInfo "Starting operation..." +Write-LogSuccess "Created resource: $resourceName" +Write-LogWarning "Resource already exists, skipping" +Write-LogError "Failed to create resource" +Write-LogDebug "Detailed debug info" -Verbose +``` + +### Log Level Guidelines + +| Level | When to Use | Example | +|-------|-------------|---------| +| **Info** | Normal operations, progress updates | "Starting deployment..." | +| **Success** | Successful resource creation/modification | "Created resource group: rg-iic-prodtech-eus-hub" | +| **Warning** | Non-critical issues, skipped operations | "Resource already exists, skipping" | +| **Error** | Failures that prevent operation | "Failed to connect to Azure" | +| **Debug** | Detailed diagnostic info (use `-Verbose`) | "Config loaded from: C:\...\infrastructure.yml" | + +### Invoke-Script File Logging + +:::info MANDATORY for all `Invoke-` scripts +All orchestrated scripts that run remotely must write log output to a file in addition to the console. This enables post-run review, audit, and troubleshooting. +::: + +**Log file location**: All scripts are executed from the **repo root**. Logs are written to the `logs/` directory at the repo root, organized by task folder name. The `logs/` folder is a sibling to `scripts/`, `configs/`, `docs/`, etc. + +``` +prodtech-docs-azl-toolkit/ # <-- repo root (CWD when running scripts) + scripts/ + deploy/04-cluster-deployment/phase-03-os-configuration/ + task-07-configure-time-synchronization-ntp/ + powershell/ + Invoke-ConfigureNTP-Orchestrated.ps1 + logs/ # <-- log output goes here + task-07-configure-time-synchronization-ntp/ + 2026-03-07_143000_ConfigureNTP.log + 2026-03-07_152200_ConfigureNTP.log +``` + +When you run: +```powershell +.\scripts\deploy\...\task-07-...\powershell\Invoke-ConfigureNTP-Orchestrated.ps1 +``` +The log lands at: +``` +.\logs\task-07-configure-time-synchronization-ntp\2026-03-07_143000_ConfigureNTP.log +``` + +**Log file naming pattern**: `__.log` + +**`Write-Log` function for `Invoke-` scripts:** + +All `Invoke-` scripts use a self-contained `Write-Log` function (not the helper-based pattern). This function **must** support: + +1. **Console output** — colored, timestamped, always visible +2. **File output** — same content written to the log file (plain text, no ANSI) +3. **Verbose output** — additional detail when `-Verbose` is passed to the script +4. **Debug output** — diagnostic detail when `-Debug` is passed to the script + +```powershell +[CmdletBinding()] +param( + [string]$ConfigPath = "", + [PSCredential]$Credential, + [string[]]$TargetNode = @(), + [switch]$WhatIf, + [string]$LogPath = "" +) + +# --- Log file initialization --- +# Scripts are always run from the repo root, so ./logs// is CWD-relative +$scriptShortName = [System.IO.Path]::GetFileNameWithoutExtension($PSCommandPath) -replace '^Invoke-|-Orchestrated$', '' +$taskFolderName = Split-Path (Split-Path $PSScriptRoot -Parent) -Leaf # e.g. task-07-configure-time-synchronization-ntp +$logDir = Join-Path (Get-Location).Path "logs\$taskFolderName" +if ($LogPath -ne "") { + $logDir = Split-Path $LogPath -Parent + $logFile = $LogPath +} else { + $logFile = Join-Path $logDir "$(Get-Date -Format 'yyyy-MM-dd_HHmmss')_${scriptShortName}.log" +} +if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } + +function Write-Log { + param([string]$Message, [string]$Level = "INFO") + $ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + $line = "[$ts] [$Level] $Message" + + # Always write to log file (all levels, including VERBOSE and DEBUG) + $line | Out-File -FilePath $script:logFile -Append -Encoding utf8 + + # Console output (colored, respects verbosity flags) + switch ($Level) { + "PASS" { Write-Host "[$ts] [PASS] $Message" -ForegroundColor Green } + "FAIL" { Write-Host "[$ts] [FAIL] $Message" -ForegroundColor Red } + "WARN" { Write-Host "[$ts] [WARN] $Message" -ForegroundColor Yellow } + "HEADER" { Write-Host "[$ts] [----] $Message" -ForegroundColor Cyan } + "VERBOSE" { Write-Verbose "[$ts] $Message" } # only shown with -Verbose + "DEBUG" { Write-Debug "[$ts] $Message" } # only shown with -Debug + default { Write-Host "[$ts] [INFO] $Message" } + } +} + +# Usage: +Write-Log "Config loaded from: $ConfigPath" # always shown +Write-Log "Node filter: $($TargetNode -join ', ')" VERBOSE # only with -Verbose +Write-Log "Raw YAML object: $($cfg | ConvertTo-Json -Depth 2)" DEBUG # only with -Debug +Write-Log "Log file: $logFile" +``` + +**Verbosity tiers:** + +| Flag | What's Shown | Use Case | +|------|-------------|----------| +| *(none)* | INFO, PASS, FAIL, WARN, HEADER | Normal execution | +| `-Verbose` | All of above + VERBOSE | Troubleshooting — shows config values, intermediate results, per-step details | +| `-Debug` | All of above + DEBUG | Deep diagnostics — shows raw objects, full error stacks, WinRM transport details | + +**Rules:** +- Log file is **always written** regardless of verbosity flags — it contains ALL levels including VERBOSE and DEBUG +- Console output respects the verbosity flags +- Log file path is printed at script start and end +- Scripts are **always run from the repo root** — log path is `./logs//_.log` relative to CWD +- The `logs/` directory is `.gitignore`d — never commit log files +- `-LogPath` overrides the default location (useful for CI/CD or centralized logging) + +--- + +## Error Handling + +All scripts must implement comprehensive error handling: + +```powershell +try { + # Risky operation + $result = New-AzResourceGroup -Name $rgName -Location $location + Write-LogSuccess "Created resource group: $rgName" +} +catch { + Write-LogError "Failed to create resource group: $rgName" + Write-LogError $_.Exception.Message + + # Re-throw if we can't recover + throw +} +``` + +### Error Handling Best Practices + +**Do:** +- Use `try/catch` blocks for all external operations (Azure, file I/O, network) +- Log meaningful error messages with context +- Include `$_.Exception.Message` for detailed error info +- Re-throw errors that can't be recovered (`throw`) +- Set `$ErrorActionPreference = "Stop"` for fail-fast behavior + +**Don't:** +- Silently swallow errors (`catch {}` with no logging) + +--- + +## WhatIf Support + +All scripts that modify resources MUST support `-WhatIf`: + +```powershell +[CmdletBinding(SupportsShouldProcess)] +param( + [Parameter(Mandatory = $true)] + [string]$Environment, + + [Parameter(Mandatory = $false)] + [switch]$WhatIf +) + +# Use $WhatIf or $PSCmdlet.ShouldProcess for conditional execution +if ($PSCmdlet.ShouldProcess($rgName, "Create Resource Group")) { + New-AzResourceGroup -Name $rgName -Location $location + Write-LogSuccess "Created resource group: $rgName" +} +else { + Write-LogInfo "[WhatIf] Would create resource group: $rgName" +} +``` + +### WhatIf Guidelines + +**Do:** +- Add `[CmdletBinding(SupportsShouldProcess)]` to enable WhatIf +- Use `$PSCmdlet.ShouldProcess()` for create/modify/delete operations +- Test with `-WhatIf` before running destructive operations +- Log what *would* happen when in WhatIf mode + +**Don't:** +- Never make changes when `-WhatIf` is specified + +### Invoke-Script Execution Modes {#invoke-script-execution-modes} + +:::danger MANDATORY for `Invoke-` scripts +`$PSCmdlet.ShouldProcess()` does **not** work inside `Invoke-Command` remote scriptblocks. `Invoke-` scripts must implement `-WhatIf` using a simple `$WhatIf` switch passed into the scriptblock as an argument. +::: + +**Pattern for `Invoke-` scripts:** + +```powershell +[CmdletBinding()] +param( + [string]$ConfigPath = "", + [PSCredential]$Credential, + [string[]]$TargetNode = @(), + [switch]$WhatIf # <-- simple switch, NOT SupportsShouldProcess +) + +# Pass $WhatIf into the remote scriptblock as an argument +$results = Invoke-Command -ComputerName $ip -Credential $cred ` + -ArgumentList $ip, $gateway, $prefixLength, $WhatIf.IsPresent ` + -ScriptBlock { + param($IP, $Gateway, $Prefix, [bool]$DryRun) + + # Read-only checks always run + $adapter = Get-NetAdapter | Where-Object { $_.Status -eq 'Up' } | Select-Object -First 1 + $currentIP = (Get-NetIPAddress -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4).IPAddress + + if ($DryRun) { + # Report what WOULD change — make no changes + if ($currentIP -ne $IP) { + "[WhatIf] Would set IP from $currentIP to $IP/$Prefix GW $Gateway" + } else { + "[WhatIf] IP already $IP — no change needed" + } + return + } + + # Actual execution + New-NetIPAddress -InterfaceIndex $adapter.ifIndex -IPAddress $IP ` + -PrefixLength $Prefix -DefaultGateway $Gateway + } +``` + +**Execution mode summary:** + +| Mode | Flag | Behavior | +|------|------|----------| +| **Normal** | *(none)* | Execute all operations, log results | +| **Dry-run** | `-WhatIf` | Read current state, log what *would* change, make **no modifications** | +| **Verbose** | `-Verbose` | Normal execution + detailed progress output | +| **Debug** | `-Debug` | Normal execution + raw diagnostic output | +| **Dry-run + Verbose** | `-WhatIf -Verbose` | Show what would change with full detail, no modifications | + +**Rules:** +- `-WhatIf` must be respected at **every** point that modifies state (IP config, service restart, hostname rename, firewall rules, etc.) +- Read-only checks (verify DNS, check hostname, query NTP status) **always execute** even in WhatIf mode +- WhatIf output must prefix messages with `[WhatIf]` so they're clearly identifiable in console and log file +- Log file is still written in WhatIf mode (captures the dry-run output for review) + +--- + +## Subscription Context + +Always set subscription context explicitly: + +```powershell +# Get subscription ID from config # azure_platform.subscriptions..id +$subscriptionId = $config.azure_platform.subscriptions.lab.id + +# Set context +Set-AzContext -SubscriptionId $subscriptionId | Out-Null +Write-LogInfo "Set context to subscription: $subscriptionId" +``` + +### Multi-Subscription Operations + +For cross-subscription operations (e.g., cross-tenant access): + +```powershell +# Save current context +$originalContext = Get-AzContext + +try { + # Switch to target subscription + Set-AzContext -SubscriptionId $targetSubscriptionId | Out-Null + + # Perform operations in target subscription + New-AzResourceGroup -Name $rgName -Location $location +} +finally { + # Always restore original context + Set-AzContext -Context $originalContext | Out-Null +} +``` + +--- + +## Idempotency + +Scripts should be safe to run multiple times (idempotent): + +```powershell +# Check if resource exists before creating +$existing = Get-AzResourceGroup -Name $rgName -ErrorAction SilentlyContinue +if ($existing) { + Write-LogWarning "Resource group already exists: $rgName" +} +else { + New-AzResourceGroup -Name $rgName -Location $location + Write-LogSuccess "Created resource group: $rgName" +} +``` + +### Idempotency Patterns + +| Operation | Pattern | +|-----------|---------| +| **Create** | Check existence first, skip if exists | +| **Update** | Compare current vs desired state, update only if different | +| **Delete** | Check existence first, skip if already deleted | +| **Configuration** | Read current config, apply only changed values | + +--- + +## Customer-Specific Scripts + +Customer deployment repos contain scripts that read from their `infrastructure.yml`: + +```powershell +# src/powershell/deploy/Initialize-ClusterPrereqs.ps1 + +# Load customer's configuration +. "$PSScriptRoot/../utilities/helpers/config-loader.ps1" +$config = Get-InfrastructureConfig + +# Read customer-specific values # compute.azure_local.* +$clusterName = $config.compute.azure_local.cluster_name # compute.azure_local.cluster_name +$nodeCount = $config.compute.azure_local.cluster_node_count # compute.azure_local.cluster_node_count +$topology = $config.compute.azure_local.network_topology # compute.azure_local.network_topology + +# Adapt logic based on customer's configuration +if ($topology -eq "switchless") { + Write-Host "Configuring switchless RDMA connections for $clusterName" +} +elseif ($topology -eq "switched") { + Write-Host "Configuring switched network topology for $clusterName" +} +``` + +**Key Point**: Each customer deployment has ONE `infrastructure.yml` file with their specific configuration values. + +--- + +## File Naming Conventions + +Follow PowerShell approved verb-noun naming: + +| Pattern | Purpose | Example | +|---------|---------|---------| +| `New-*.ps1` | Create new resources | `New-AzureLocalSP.ps1` | +| `Get-*.ps1` | Retrieve/discover info | `Get-TenantInfo.ps1`, `Get-ClusterInventory.ps1` | +| `Set-*.ps1` | Modify existing resources | `Set-RoleAssignments.ps1` | +| `Remove-*.ps1` | Delete resources | `Remove-ServicePrincipal.ps1` | +| `Test-*.ps1` | Validate/check | `Test-ConfigSchema.ps1`, `Test-ClusterHealth.ps1` | +| `Initialize-*.ps1` | Setup/bootstrap | `Initialize-KeyVault.ps1` | +| `Export-*.ps1` | Export data | `Export-ToExcel.ps1` | +| `Import-*.ps1` | Import data | `Import-FromYAML.ps1` | +| `Deploy-*.ps1` | Deploy infrastructure | `Deploy-NetworkInfrastructure.ps1` | +| `Configure-*.ps1` | Configure resources | `Configure-ActiveDirectory.ps1` | + +**Naming Rules:** + +**Do:** +- Use PascalCase for script names +- Use approved PowerShell verbs ([Get-Verb](https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands)) +- Use descriptive nouns that indicate resource type +- Use singular nouns (e.g., `Get-Cluster.ps1`, not `Get-Clusters.ps1`) + +**Don't:** +- Use abbreviations unless standard (e.g., `SP` for Service Principal) + +--- + +## Testing Scripts + +Before committing, verify: + +### 1. Static Analysis + +```powershell +# Install PSScriptAnalyzer if not already installed +Install-Module -Name PSScriptAnalyzer -Scope CurrentUser -Force + +# Run analysis +Test-ScriptAnalyzer -Path .\script.ps1 +``` + +### 2. WhatIf Mode + +```powershell +# Always test with WhatIf first +.\Deploy-Infrastructure.ps1 -ConfigPath "configs/infrastructure.yml" -WhatIf +``` + +### 3. Idempotency + +```powershell +# Run twice - second run should be no-op +.\Deploy-Infrastructure.ps1 -ConfigPath "configs/infrastructure.yml" +.\Deploy-Infrastructure.ps1 -ConfigPath "configs/infrastructure.yml" # Should skip existing resources +``` + +### 4. Different Configurations + +Test with different config files to verify portability: + +```powershell +# Test with a 2-node switchless configuration +.\Get-ClusterInventory.ps1 -ConfigPath "configs/infrastructure.yml" + +# Test with an alternate configuration +.\Get-ClusterInventory.ps1 -ConfigPath "configs/infrastructure-alt.yml" +``` + +### 5. Pester Tests + +Create Pester tests for complex scripts: + +```powershell +# tests/unit/Get-ClusterInventory.Tests.ps1 +Describe "Get-ClusterInventory" { + It "Should load config from infrastructure.yml" { + $config = Get-InfrastructureConfig -ConfigPath "configs/infrastructure.yml" + $config.cluster.name | Should -Not -BeNullOrEmpty + } + + It "Should return cluster nodes" { + $nodes = Get-ClusterInventory -ConfigPath "configs/infrastructure.yml" + $nodes.Count | Should -BeGreaterThan 0 + } +} +``` + +--- + +## Script Organization in Repositories + +Follow the [PowerShell Organization Standard](./powershell-organization-standard.mdx#standard-folder-structure): + +``` +src/powershell/ +├── README.md +├── discovery/ # Data collection from live environments +│ ├── Get-DellServerInventory.ps1 +│ └── Get-AzureTenantInfo.ps1 +├── generators/ # Documentation/config generation +│ ├── Generate-ClusterConfigMD.ps1 +│ └── Generate-FromYAML.ps1 +├── deploy/ # Deployment automation +│ ├── Deploy-Infrastructure.ps1 +│ └── Deploy-AzureResources.ps1 +├── configure/ # Post-deployment configuration +│ ├── Configure-ActiveDirectory.ps1 +│ └── Configure-WAC.ps1 +├── utilities/ # Reusable functions/helpers +│ └── helpers/ +│ ├── config-loader.ps1 +│ ├── keyvault-helper.ps1 +│ └── logging.ps1 +└── ci-cd/ # Pipeline integration + ├── Build.ps1 + └── Test.ps1 +``` + +--- + +## Code Quality Checklist + +Before committing any PowerShell script: + +- [ ] **Comment-based help** - Complete `.SYNOPSIS`, `.DESCRIPTION`, `.PARAMETER`, `.EXAMPLE`, `.NOTES` +- [ ] **Parameter validation** - `ValidateScript` for ConfigPath parameter +- [ ] **Credential resolution** (`Invoke-` scripts only) - Implements `-Credential` param → Key Vault (`Resolve-KeyVaultRef`) → `Get-Credential` fallback, in that order +- [ ] **Error handling** - `try/catch` blocks for all risky operations +- [ ] **Logging** - Use standard logging functions (`Write-LogInfo`, etc.) +- [ ] **WhatIf support** - `[CmdletBinding(SupportsShouldProcess)]` for modify operations +- [ ] **Idempotency** - Check existence before create, safe to run multiple times +- [ ] **Relative paths** - Use `$PSScriptRoot` for portability +- [ ] **Config-driven** - Read all values from `infrastructure.yml` via `Get-InfrastructureConfig` +- [ ] **Subscription context** - Set explicitly using `Set-AzContext` +- [ ] **Static analysis** - Passes `Test-ScriptAnalyzer` with no errors +- [ ] **Tested** - Verified with WhatIf, idempotency check, and multiple environments +- [ ] **Named correctly** - Follows approved verb-noun pattern with PascalCase + +--- + +## Onboarding + +### Getting Started + +New contributors should follow these steps to set up their development environment: + +1. **Clone the repository** + ```powershell + git clone + cd + ``` + +2. **Install required tools** + - [PowerShell 7+](https://github.com/PowerShell/PowerShell/releases) + - [Visual Studio Code](https://code.visualstudio.com/) + - [Git](https://git-scm.com/downloads) + +3. **Install VS Code extensions** + - PowerShell (ms-vscode.powershell) + - YAML (redhat.vscode-yaml) + - ShellCheck (timonwong.shellcheck) + +4. **Install PowerShell modules** + ```powershell + Install-Module -Name Az -Scope CurrentUser + Install-Module -Name powershell-yaml -Scope CurrentUser + Install-Module -Name PSScriptAnalyzer -Scope CurrentUser + Install-Module -Name Pester -Scope CurrentUser + ``` + +5. **Install ShellCheck** (for Bash scripts) + - Windows: `choco install shellcheck` or download from [GitHub](https://github.com/koalaman/shellcheck/releases) + - Linux/Mac: `apt-get install shellcheck` or `brew install shellcheck` + +6. **Run the quickstart script** + ```powershell + .\scripts\onboarding\Setup-DevEnv.ps1 + ``` + This script: + - Validates your environment setup + - Installs missing tools and modules + - Configures VS Code settings + - Runs initial tests to ensure everything works + +### Using the Script Template Generator + +To create a new standards-compliant script: + +```powershell +.\scripts\tools\New-ScriptFromTemplate.ps1 -ScriptType PowerShell -Name "New-AzureResource" -Description "Creates Azure resources" +``` + +**Supported script types:** +- `PowerShell` - PowerShell Core scripts (Verb-Noun.ps1) +- `AzurePowerShell` - Azure PowerShell scripts (Verb-AzResource.ps1) +- `AzureCliPowerShell` - Azure CLI for PowerShell (az-verb-resource.ps1) +- `AzureCliBash` - Azure CLI Bash scripts (az-verb-resource.sh) +- `InvokeScript` - Remote/management-box scripts (Invoke-Action.ps1) + +The generator will: +- Create the script file with proper naming +- Add comment-based help template +- Include config loading and helper imports +- Add parameter validation and error handling +- Place the script in the correct directory + +### Running Scripts with Configuration + +All scripts accept a config/environment parameter: + +```powershell +# Using config file path (default: configs/infrastructure.yml) +.\scripts\deploy\New-AzureResource.ps1 -ConfigPath "configs/infrastructure.yml" + +# Using custom config path +.\scripts\deploy\New-AzureResource.ps1 -ConfigPath "C:\custom\path\infrastructure.yml" +``` + +### Testing and Validation + +**Run PSScriptAnalyzer:** +```powershell +Invoke-ScriptAnalyzer -Path .\scripts\deploy\New-AzureResource.ps1 +``` + +**Run ShellCheck (Bash):** +```bash +shellcheck scripts/deploy/az-create-vnet.sh +``` + +**Run Pester tests:** +```powershell +Invoke-Pester -Path .\scripts\tests\ +``` + +### CI/Pre-Commit Hooks + +The repository includes automated checks that run on commit and in CI pipelines: + +**Pre-commit hooks enforce:** +- Script naming conventions (correct prefix, verb-noun pattern) +- No hardcoded values (must use config variables) +- Comment-based help presence and completeness +- PSScriptAnalyzer/ShellCheck passing +- Pester test coverage + +**To set up pre-commit hooks:** +```powershell +.\scripts\tools\Install-PreCommitHooks.ps1 +``` + +**CI pipeline checks:** +- All scripts pass PSScriptAnalyzer with zero errors +- All scripts pass ShellCheck (Bash) +- All scripts have required parameters (Environment/ConfigPath) +- All scripts use helpers (config-loader, logging, etc.) +- All Pester tests pass + +--- + +## Resources + +### Official Documentation + +- **PowerShell Best Practices**: [Microsoft Docs](https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/strongly-encouraged-development-guidelines) +- **Approved Verbs**: [Get-Verb Reference](https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands) +- **PSScriptAnalyzer**: [GitHub](https://github.com/PowerShell/PSScriptAnalyzer) +- **Pester Testing**: [Pester Docs](https://pester.dev/) + +### Related Standards + +- [PowerShell Organization Standard](./powershell-organization-standard.mdx) - PowerShell organization in repositories +- [Naming Conventions](../documentation/naming-conventions.mdx) - File and resource naming standards +- [Scripting Framework](./scripting-framework.mdx) - Script templates and patterns + +--- + +**Version Control** + +- Created: 2025-12-10 by Product Technology Team +- Last Edited: 2026-02-08 by Product Technology Team +- Version: 1.1.0 +- Tags: standards, powershell, scripting, automation +- Keywords: powershell, scripting, automation, standards, best-practices, azure +- Author: Product Technology Team + diff --git a/standards/solutions/index.mdx b/standards/solutions/index.mdx new file mode 100644 index 000000000..b7d3bb7a9 --- /dev/null +++ b/standards/solutions/index.mdx @@ -0,0 +1,46 @@ +--- +title: Solution Standards +sidebar_label: Solutions +sidebar_position: 7 +description: "Standards for solution packaging, multi-tool parity, and deployment automation" +--- + +# Solution Standards + +[![Solutions](https://img.shields.io/badge/Type-Solutions-purple?style=flat-square&logo=microsoftazure)](./solution-development-standard.mdx) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +> **DOCUMENT CATEGORY**: Standards Index +> **SCOPE**: Solution packaging, automation tool parity, and deployment standards +> **PURPOSE**: Define how solutions are structured, maintained, and deployed across all automation toolchains +> **MASTER REFERENCE**: [Documentation Standards](../documentation/documentation-standards.mdx) + +**Status**: Active +**Applies To**: All solution packages in the Azure Local toolkit +**Last Updated**: 2026-03-14 + +--- + +Standards for solution development, multi-tool automation parity, and deployment best practices. + +## Overview + +This section covers: + +- **Solution structure** — Directory layout, config chain, and required artifacts +- **Multi-tool parity** — Keeping Terraform, PowerShell, Bicep, and ARM templates in sync +- **Parallelism** — Running independent operations concurrently for faster deployments +- **Idempotency** — Ensuring scripts are safe to re-run without side effects +- **Variable governance** — Single source of truth from infrastructure YAML through solution config to scripts + +## Standards in This Section + +import DocCardList from '@theme/DocCardList'; + + + +## Quick Reference + +| Standard | Purpose | +|----------|---------| +| [Solution Development Standard](./solution-development-standard.mdx) | End-to-end rules for building and maintaining solutions | diff --git a/standards/solutions/solution-development-standard.mdx b/standards/solutions/solution-development-standard.mdx new file mode 100644 index 000000000..bcadc25db --- /dev/null +++ b/standards/solutions/solution-development-standard.mdx @@ -0,0 +1,521 @@ +--- +id: solution-development-standard +title: Solution Development Standard +sidebar_label: Solution Development +sidebar_position: 1 +description: "Standards for solution packaging, multi-tool automation parity, parallelism, and deployment governance" +--- + +# Solution Development Standard + +[![Standards](https://img.shields.io/badge/Type-Standard-purple?style=flat-square)](../documentation/documentation-standards.mdx) +[![Solutions](https://img.shields.io/badge/Category-Solutions-blue?style=flat-square)](./index.mdx) +[![TierPoint](https://img.shields.io/badge/TierPoint-ProdTech-orange?style=flat-square)](https://tierpoint.com) + +> **DOCUMENT CATEGORY**: Standard +> **SCOPE**: All solution packages in the Azure Local toolkit repository +> **PURPOSE**: Define rules for building, maintaining, and deploying solutions across all automation toolchains +> **MASTER REFERENCE**: [Documentation Standards](../documentation/documentation-standards.mdx) + +**Status**: Active +**Applies To**: All solution directories under `solutions/` in the azl-toolkit repository +**Last Updated**: 2026-03-13 + +--- + +## Purpose + +A **solution** is a deployable workload package that provisions and configures cloud or hybrid infrastructure for a specific use case (e.g., SOFS for FSLogix, AVD session hosts, AKS on Azure Local). Each solution lives under `solutions//` in the azl-toolkit repository. + +This standard defines the rules that **every** solution must follow — from directory structure to multi-tool parity to deployment behavior. + +:::info Relationship to Other Standards +This standard builds on: +- [Scripting Standards](../scripting/scripting-standards.mdx) — PowerShell conventions, credential resolution, logging +- [Infrastructure Standards](../infrastructure/infrastructure-generation-deployment-process.mdx) — IaC patterns, state management +- [Variable Management Standards](../variable-management/) — Registry, schema, naming rules +- [Provisioning Runbook Standard](../provisioning/provisioning-runbook-standard.mdx) — Documentation structure for deployment guides +::: + +--- + +## 1. Solution Directory Structure + +Every solution **must** follow this directory layout: + +```text +solutions// +├── solution-.yml # Generated solution config (DO NOT hand-edit) +├── README.md # Solution overview, prerequisites, quick-start +├── powershell/ # PowerShell automation scripts +│ ├── Deploy--Azure.ps1 # Infrastructure deployment (az CLI / Az modules) +│ └── Configure--*.ps1 # Guest OS / workload configuration +├── terraform/ # Terraform automation (when applicable) +│ ├── main.tf +│ ├── variables.tf +│ ├── terraform.tfvars # MUST match infrastructure YAML values — see §3 +│ └── ... +├── bicep/ # Bicep templates (when applicable) +│ ├── main.bicep +│ └── parameters.json +└── arm/ # ARM templates (when applicable) + ├── template.json + └── parameters.json +``` + +:::warning Generated Config Is Read-Only +The `solution-.yml` file is **generated** by `tools/Generate-SolutionConfig.ps1` from the infrastructure YAML and `configs/solutions.yaml`. Never hand-edit it. If a value is wrong, fix it in `configs/infrastructure-.yml` and regenerate. +::: + +--- + +## 2. Multi-Tool Automation Parity + +### 2.1 The Parity Rule + +> **Every automation tool provided for a solution MUST produce the same infrastructure outcome.** + +When a solution ships multiple automation options (PowerShell, Terraform, Bicep, ARM), all options must: + +- Create the **same resources** with the **same configuration** +- Use the **same variable values** from the same source of truth +- Support the **same features** (e.g., per-VM storage paths, static IPs, tagging) +- Produce **equivalent outputs** (resource IDs, connection strings, etc.) + +A user switching from Terraform to PowerShell (or vice versa) should get an identical deployment. + +### 2.2 Sync-on-Change Rule + +:::danger MANDATORY +When a feature, variable, or configuration change is made to **any** automation tool for a solution, **all other automation tools for that solution must be updated in the same commit or merge request.** +::: + +| Change Type | What Must Be Updated | +|-------------|---------------------| +| New variable added | master-registry.yaml → infrastructure YAML → solutions.yaml → **ALL** automation tools (PS, TF, Bicep, ARM) | +| Variable value changed | infrastructure YAML → regenerate solution config → verify **ALL** tools read the new value | +| New resource added | **ALL** automation tools must create the resource with identical config | +| Resource property changed | **ALL** automation tools must reflect the change | +| Feature added (e.g., per-VM storage paths) | **ALL** automation tools must implement the feature | + +### 2.3 Parity Review Checklist + +Every merge request that modifies a solution **must** include this checklist in the MR description: + +```markdown +## Solution Parity Checklist +- [ ] PowerShell scripts updated and tested +- [ ] Terraform configs updated and `terraform plan` clean +- [ ] Bicep templates updated (if applicable) +- [ ] ARM templates updated (if applicable) +- [ ] master-registry.yaml updated (if new variables) +- [ ] infrastructure YAML updated +- [ ] solutions.yaml updated (if new required variables) +- [ ] solution-.yml regenerated +- [ ] All tools produce equivalent `--what-if` / `-WhatIf` / `plan` output +``` + +### 2.4 Single Source of Truth + +The config chain is **non-negotiable**: + +```text +master-registry.yaml ← Variable definitions (type, validation, description) + ↓ +infrastructure-.yml ← Environment-specific values + ↓ +solutions.yaml ← Solution → variable mapping + ↓ +Generate-SolutionConfig.ps1 ← Assembles the solution config + ↓ +solution-.yml ← What scripts actually consume (GENERATED, READ-ONLY) +``` + +**Rules:** +- Scripts read from `solution-.yml` — never directly from `infrastructure-.yml` +- Terraform `terraform.tfvars` values **must** match the infrastructure YAML — if they drift, the infrastructure YAML wins +- No automation tool may define its own variables that bypass this chain +- Every `$cfg.*` access in scripts must have an inline comment with the YAML path + +--- + +## 3. Variable Governance for Solutions + +### 3.1 No Orphan Variables + +Every variable used by a solution must exist in: + +1. `configs/variables/assets/master-registry.yaml` — with type, description, and validation rules +2. `configs/infrastructure-.yml` — with the environment-specific value +3. `configs/solutions.yaml` — listed in the solution's `required_variables` array + +If a variable exists in a Terraform `tfvars` or a PowerShell script but **not** in the registry, it is an orphan and must be registered. + +### 3.2 Variable Naming + +All solution-specific variables follow the pattern: + +```text +wsfc_sofs_ # WSFC SOFS solution +avd_ # Azure Virtual Desktop solution +aks_ # AKS solution +``` + +Variable naming rules from the [Variable Management Standards](../variable-management/) apply in full: `snake_case`, `^[a-z][a-z0-9_]*$`, max 50 characters. + +### 3.3 Map Variables for Per-Resource Configuration + +When a property varies per resource instance (e.g., per-VM storage paths), use a **map** variable type: + +```yaml title="infrastructure-.yml" +wsfc_sofs_storage_path_ids: + "01": "/subscriptions/.../sp-clus01-vmstore-prd-01" + "02": "/subscriptions/.../sp-clus01-vmstore-prd-02" + "03": "/subscriptions/.../sp-clus01-vmstore-prd-03" +``` + +Scripts must iterate the map by key — never hardcode per-instance values. + +--- + +## 4. Parallel Execution + +### 4.1 The Parallelism Rule + +> **Independent operations MUST be executed in parallel to minimize deployment time.** + +When deploying multiple identical resources (VMs, NICs, disks), operations that have no dependencies on each other should run concurrently. + +### 4.2 Identifying Parallel Candidates + +| Pattern | Parallel? | Reason | +|---------|-----------|--------| +| Creating 3 VMs with no inter-dependencies | **Yes** | Each VM is independent | +| Creating 3 NICs on the same logical network | **Yes** | NICs are independent | +| Creating data disks for the same VM | **Yes** | Disks are independent | +| Creating a VM then attaching disks to it | **No** | Disk attach depends on VM existing | +| Creating a NIC then creating the VM that uses it | **No** | VM depends on NIC | +| Installing roles on 3 nodes via PSRemoting | **Yes** | Each node is independent | +| Creating a cluster then configuring S2D | **No** | S2D depends on cluster | + +### 4.3 Implementation Patterns + +#### PowerShell — `ForEach-Object -Parallel` + +Use PowerShell 7's `-Parallel` for independent Azure CLI operations: + +```powershell title="Example: Create 3 NICs in parallel" +$VMNames | ForEach-Object -Parallel { + $VMName = $_ + $NicName = "$VMName-nic" + # Each iteration runs in its own runspace + az stack-hci-vm network nic create ` + --name $NicName ` + --resource-group $using:ResourceGroup ` + --custom-location $using:CustomLocationId ` + --location $using:Location ` + --subnet-id $using:LogicalNetworkId ` + --ip-address $using:VMIPs[$VMName] +} -ThrottleLimit 5 +``` + +#### PowerShell — `Start-Job` / `Wait-Job` + +For operations that need output capture: + +```powershell title="Example: Create 3 VMs as background jobs" +$jobs = @() +foreach ($VM in $VMNames) { + $jobs += Start-Job -ScriptBlock { + param($VMName, $RG, $Location, $Image) + az stack-hci-vm create --name $VMName --resource-group $RG ... + } -ArgumentList $VM, $ResourceGroup, $Location, $ImageName +} +$jobs | Wait-Job | Receive-Job +``` + +#### Terraform — Implicit Parallelism + +Terraform handles parallelism automatically when resources have no `depends_on` or implicit references between them. Use `count` or `for_each` for multi-instance resources: + +```hcl title="Example: Terraform parallel VM creation" +resource "azurerm_stack_hci_virtual_machine" "sofs" { + for_each = var.vm_configs + name = each.value.name + # Terraform creates all VMs in parallel (up to -parallelism flag) +} +``` + +:::tip Terraform Parallelism Flag +Increase parallelism for large deployments: `terraform apply -parallelism=10` (default is 10). +::: + +#### Bicep — Batch Deployments + +Bicep supports `@batchSize()` for controlling parallel module deployments: + +```bicep title="Example: Bicep parallel NIC creation" +@batchSize(3) +module nics 'nic.bicep' = [for (vm, i) in vmConfigs: { + name: '${vm.name}-nic' + params: { ... } +}] +``` + +### 4.4 Parallelism Requirements by Step + +Each solution must document which deployment steps are parallelizable. Example for a 3-node SOFS: + +| Step | Operation | Parallel | Implementation | +|------|-----------|----------|----------------| +| Resource Group | `az group create` | N/A | Single operation | +| Cloud Witness | Storage account create | N/A | Single operation | +| NICs | Create 3 NICs | **Yes** | `ForEach-Object -Parallel` | +| VMs | Create 3 VMs | **Yes** | `ForEach-Object -Parallel` | +| Data Disks | Create 12 disks | **Yes** | `ForEach-Object -Parallel` | +| Disk Attach | Attach 4 disks per VM | **Per-VM** | Parallel across VMs, serial within | +| Role Install | Install features on 3 nodes | **Yes** | `Invoke-Command` with multiple targets | +| Cluster Create | `New-Cluster` | No | Sequential — single operation | +| S2D Enable | `Enable-ClusterStorageSpacesDirect` | No | Sequential — depends on cluster | + +--- + +## 5. Idempotency + +### 5.1 The Idempotency Rule + +> **Every deployment script MUST be idempotent — safe to run multiple times without creating duplicates or errors.** + +### 5.2 Implementation Pattern + +Before every resource creation, check if it already exists: + +```powershell title="Idempotency guard pattern" +# Check before create +$existing = az stack-hci-vm show --name $VMName --resource-group $RG 2>$null | ConvertFrom-Json +if ($existing) { + Write-Log "VM '$VMName' already exists. Skipping." "PASS" +} else { + Write-Log "Creating VM: $VMName" + az stack-hci-vm create --name $VMName ... +} +``` + +For Terraform, idempotency is built-in via state management. For PowerShell and CLI scripts, **every** create operation must have an existence check. + +### 5.3 Idempotency Checklist + +| Resource Type | Check Command | +|--------------|---------------| +| Resource Group | `az group exists --name $RG` | +| Storage Account | `az storage account show --name $Name` | +| NIC | `az stack-hci-vm network nic show --name $Name --resource-group $RG` | +| VM | `az stack-hci-vm show --name $Name --resource-group $RG` | +| Disk | `az stack-hci-vm disk show --name $Name --resource-group $RG` | +| Failover Cluster | `Get-Cluster -ErrorAction SilentlyContinue` | +| S2D Volume | `Get-VirtualDisk -FriendlyName $Name -ErrorAction SilentlyContinue` | +| SOFS Role | `Get-ClusterGroup -Name $Name -ErrorAction SilentlyContinue` | +| SMB Share | `Get-SmbShare -Name $Name -ScopeName $Scope -ErrorAction SilentlyContinue` | + +--- + +## 6. Credential Management + +### 6.1 Zero Interactive Prompts + +> **Solution scripts MUST NEVER prompt for credentials interactively during unattended execution.** + +The credential resolution order for all solution scripts: + +1. **`-Credential` parameter** — passed directly at invocation +2. **Key Vault** — resolve from `keyvault://` URIs in the solution config (Az.KeyVault first, az CLI fallback) +3. **Hard fail** — throw a terminating error with a clear message explaining how to provide credentials + +:::danger No Read-Host, No Get-Credential Without -UserName +`Read-Host` and bare `Get-Credential` (without a pre-filled username) are **prohibited** in solution scripts. These block CI/CD pipelines and unattended execution. If Key Vault resolution fails and no `-Credential` was provided, the script must **fail loudly** — not silently wait for input. +::: + +### 6.2 Credential Scope + +| Script Purpose | Account | Config Path | +|---------------|---------|-------------| +| Azure resource deployment (az CLI) | Logged-in Azure identity | `az login` session | +| Guest OS config (pre-domain-join) | Local Administrator | `identity.accounts.account_local_admin_*` | +| Guest OS config (post-domain-join) | LCM domain account | `identity.accounts.account_lcm_*` | +| Solution-specific admin | Solution admin | Solution-specific KV secret | + +--- + +## 7. WhatIf / Dry-Run Support + +### 7.1 Requirement + +> **Every solution deployment script MUST support a `-WhatIf` (PowerShell) or equivalent dry-run mode.** + +Dry-run mode must: +- Load and validate all configuration +- Resolve credentials (to verify Key Vault access) +- Display what **would** be created/modified +- Exit without making any changes + +### 7.2 Implementation + +```powershell title="WhatIf gate pattern" +if ($WhatIf) { + Write-Log "[DRY RUN] Would create the following:" "WARN" + Write-Log " Resource Group: $ResourceGroup" + Write-Log " VMs: $VMCount x $VMPrefix" + # ... list all resources + Write-Log "[DRY RUN] No changes made." "WARN" + exit 0 +} +``` + +For Terraform, use `terraform plan`. For Bicep/ARM, use `az deployment group what-if`. + +--- + +## 8. Tagging and Metadata + +### 8.1 Required Tags + +Every Azure resource created by a solution must include these tags: + +| Tag Key | Source | Example | +|---------|--------|---------| +| `environment` | infrastructure YAML | `production` | +| `solution` | solutions.yaml | `sofs-azure-local` | +| `project` | solution config | `SOFS` | +| `workload` | solution config | `FSLogix` | + +### 8.2 Tag Consistency + +Tags must be consistent across all automation tools. Define tag values in the infrastructure YAML and pass them through the solution config — never hardcode tags in scripts or Terraform. + +--- + +## 9. Logging and Observability + +### 9.1 Log Requirements + +All solution scripts must produce structured logs: + +- **Log file**: `logs//_.log` +- **Log format**: `[] [] ` +- **Levels**: `PASS`, `FAIL`, `WARN`, `HEADER`, `INFO`, `VERBOSE`, `DEBUG` +- **Console + file**: All log output goes to both console (color-coded) and log file + +### 9.2 Log Retention + +Log files are written to the repo-root-relative `logs/` directory, which is gitignored. Engineers should archive logs from production deployments according to their retention policy. + +--- + +## 10. Error Handling and Recovery + +### 10.1 Fail-Fast on Critical Errors + +If a critical resource fails to create (e.g., the resource group, a VM), the script must: + +1. Log the error with level `FAIL` +2. Include the Azure error message/code in the log +3. Terminate execution with a non-zero exit code + +### 10.2 Graceful Degradation for Non-Critical Errors + +Non-critical operations (e.g., anti-affinity rules, AV exclusion guidance) should: + +1. Log the error with level `WARN` +2. Continue execution +3. Include the skipped step in the summary output + +### 10.3 Resumability + +Because all operations are idempotent (§5), a failed script can be re-run after fixing the root cause. The script will skip already-completed steps and resume from the failure point. + +--- + +## 11. Solution Documentation Requirements + +Every solution must include documentation in the docs site: + +| Document | Location | Purpose | +|----------|----------|---------| +| Solution overview | `docs/product-/solutions//` | Architecture, prerequisites, design decisions | +| Deployment guide | Same location | Step-by-step with Execution Options tabs | +| Variable reference | Same location or inline | All solution-specific variables with descriptions | +| Troubleshooting | Same location | Common failure modes and remediation | + +Documentation must follow the [Provisioning Runbook Standard](../provisioning/provisioning-runbook-standard.mdx) for step-by-step guides. + +--- + +## 12. Testing and Validation + +### 12.1 Pre-Deployment Validation + +Every solution script should validate prerequisites before attempting deployment: + +- Config file exists and parses correctly +- Required variables are present and non-empty +- Azure subscription context is set +- Key Vault is accessible (credential resolution succeeds) +- Required CLI extensions are installed + +### 12.2 Post-Deployment Validation + +Every deployment script must include a verification step that confirms: + +- All expected resources exist +- Resources are in the expected provisioning state +- Resource properties match the input configuration (e.g., disk count, VM size) + +### 12.3 WhatIf Testing + +Before merging any solution change, run all automation tools in dry-run mode and verify the output matches: + +```bash title="Parity validation" +# PowerShell +.\solutions\sofs\powershell\Deploy-SOFS-Azure.ps1 -WhatIf + +# Terraform +terraform plan -var-file="terraform.tfvars" + +# Bicep (if applicable) +az deployment group what-if --template-file main.bicep --parameters @parameters.json +``` + +All tools must report the same set of resources and configuration values. + +--- + +## Appendix A: Current Solutions + +| Solution | Directory | Automation Tools | Status | +|----------|-----------|-----------------|--------| +| SOFS (FSLogix) | `solutions/sofs/` | PowerShell, Terraform | Active | +| AVD | `solutions/avd/` | — | Planned | +| AKS | `solutions/aks/` | — | Planned | +| AI Foundry | `solutions/ai_foundry/` | — | Planned | +| App Services | `solutions/app_services/` | — | Planned | +| IoT Operations | `solutions/iot_operations/` | — | Planned | +| Machine Learning | `solutions/machine_learning/` | — | Planned | +| MI SQL | `solutions/mi_sql/` | — | Planned | + +--- + +## Appendix B: Example — SOFS Parity Matrix + +Demonstrates how the SOFS solution maintains parity across PowerShell and Terraform: + +| Feature | PowerShell | Terraform | +|---------|-----------|-----------| +| Resource group creation | `az group create` | `azurerm_resource_group` | +| Per-VM storage paths | `$StoragePathIds` hashtable | `storage_path_ids` map variable | +| Static IPs | `--ip-address` on NIC create | `ip_configuration.private_ip_address` | +| Data disk attach | `az stack-hci-vm disk attach` | `azurerm_stack_hci_virtual_machine_data_disk` | +| Idempotency | `az show` before each create | Terraform state | +| Credential resolution | Key Vault → hard fail | N/A (runs as service principal) | +| Dry-run mode | `-WhatIf` switch | `terraform plan` | +| Cloud witness | `az storage account create` | `azurerm_storage_account` | +| Tagging | `$Tags` hashtable → `--tags` | `tags` variable | diff --git a/standards/variable-management/assets/infrastructure.schema.json b/standards/variable-management/assets/infrastructure.schema.json new file mode 100644 index 000000000..53f240aa3 --- /dev/null +++ b/standards/variable-management/assets/infrastructure.schema.json @@ -0,0 +1,837 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://github.com/iic/azl-env/schemas/infrastructure.schema.json", + "title": "Azure Local Infrastructure Configuration", + "description": "JSON Schema for validating Azure Local environment infrastructure.yml files against master variable registry definitions. This schema enforces variable naming, types, constraints, and formats across all environments (iic-production, iic-reference, iic-poc, iic-productlabs).", + "type": "object", + "required": [ + "azure_identity", + "azure_local", + "cluster_nodes", + "network_intents", + "onprem", + "credentials", + "environment", + "site" + ], + "properties": { + "azure_identity": { + "type": "object", + "description": "Azure AD/Entra ID tenant and subscription identification", + "required": [ + "azure_tenant_id", + "azure_subscription_id" + ], + "properties": { + "azure_tenant_id": { + "type": "string", + "description": "Azure AD/Entra ID Tenant ID (GUID)", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "examples": [ + "12345678-1234-1234-1234-123456789012" + ] + }, + "azure_subscription_id": { + "type": "string", + "description": "Azure Subscription ID for resource deployment", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "examples": [ + "87654321-4321-4321-4321-210987654321" + ] + }, + "azure_client_id": { + "type": "string", + "description": "Service Principal / App Registration Client ID", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "examples": [ + "00000000-aaaa-bbbb-cccc-333333333333" + ] + } + }, + "additionalProperties": false + }, + "azure_resources": { + "type": "object", + "description": "Azure resource configuration", + "properties": { + "resource_group_name": { + "type": "string", + "description": "Name of the Azure Resource Group", + "pattern": "^rg-[a-z0-9-]+$", + "maxLength": 90, + "examples": [ + "rg-azlc-iic-eus-connectivity-hub" + ] + }, + "location": { + "type": "string", + "description": "Azure region for resource deployment", + "enum": [ + "eastus", + "eastus2", + "westus", + "westus2", + "westus3", + "centralus", + "northcentralus", + "southcentralus", + "westeurope", + "northeurope", + "uksouth", + "ukwest" + ], + "examples": [ + "eastus" + ] + }, + "tags": { + "type": "object", + "description": "Azure resource tags for governance and cost management", + "properties": { + "Environment": { + "type": "string" + }, + "Project": { + "type": "string" + }, + "ManagedBy": { + "type": "string" + }, + "Owner": { + "type": "string" + } + }, + "additionalProperties": true + } + }, + "additionalProperties": false + }, + "networking": { + "type": "object", + "description": "Azure Virtual Network configuration", + "properties": { + "vnet_name": { + "type": "string", + "description": "Virtual Network name following Azure CAF naming standards", + "pattern": "^vnet-[a-z0-9-]+$", + "examples": [ + "vnet-azlc-iic-eus-connectivity-hub" + ] + }, + "vnet_address_space": { + "type": "array", + "description": "Address space(s) for the Virtual Network in CIDR notation", + "items": { + "type": "string", + "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}\\/([0-9]|[1-2][0-9]|3[0-2])$" + }, + "minItems": 1, + "examples": [ + [ + "10.250.0.0/16" + ] + ] + }, + "subnet_name": { + "type": "string", + "description": "Subnet name within the VNet", + "pattern": "^snet-[a-z0-9-]+$", + "examples": [ + "snet-azlc-iic-eus-connectivity-hub" + ] + }, + "subnet_address_prefix": { + "type": "string", + "description": "Subnet address prefix in CIDR notation", + "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}\\/([0-9]|[1-2][0-9]|3[0-2])$", + "examples": [ + "10.100.1.0/24" + ] + } + }, + "additionalProperties": false + }, + "keyvault": { + "type": "object", + "description": "Azure Key Vault configuration", + "required": [ + "platform_keyvault_name" + ], + "properties": { + "platform_keyvault_name": { + "type": "string", + "description": "Platform Key Vault name (azl-platform-kv) for shared secrets", + "pattern": "^[a-zA-Z][a-zA-Z0-9-]{1,22}[a-zA-Z0-9]$", + "minLength": 3, + "maxLength": 24, + "examples": [ + "azl-platform-kv" + ] + }, + "keyvault_sku": { + "type": "string", + "description": "Key Vault SKU tier", + "enum": [ + "standard", + "premium" + ], + "default": "standard" + } + }, + "additionalProperties": false + }, + "storage": { + "type": "object", + "description": "Azure Storage Account configuration", + "properties": { + "storage_account_name": { + "type": "string", + "description": "Azure Storage Account name (globally unique, lowercase alphanumeric)", + "pattern": "^[a-z0-9]{3,24}$", + "examples": [ + "stazlciiceus001" + ] + }, + "storage_account_sku": { + "type": "string", + "description": "Storage Account replication SKU", + "enum": [ + "Standard_LRS", + "Standard_GRS", + "Standard_RAGRS", + "Standard_ZRS", + "Premium_LRS", + "Premium_ZRS" + ], + "default": "Standard_LRS" + } + }, + "additionalProperties": false + }, + "azure_local": { + "type": "object", + "description": "Azure Local / HCI cluster configuration", + "required": [ + "cluster_name", + "cluster_node_count", + "network_topology", + "identity_provider", + "arc_resource_group", + "witness_type", + "cluster_ip" + ], + "properties": { + "cluster_name": { + "type": "string", + "description": "Azure Local cluster name (max 15 characters)", + "pattern": "^[a-zA-Z][a-zA-Z0-9-]*$", + "maxLength": 15, + "examples": [ + "iic-01-clus01" + ] + }, + "cluster_node_count": { + "type": "integer", + "description": "Number of nodes in the Azure Local cluster", + "minimum": 1, + "maximum": 16, + "examples": [ + 2 + ] + }, + "network_topology": { + "type": "string", + "description": "Network topology for Azure Local deployment", + "enum": [ + "switchless", + "traditional", + "nested" + ], + "examples": [ + "switchless" + ] + }, + "identity_provider": { + "type": "string", + "description": "Identity provider for cluster authentication", + "enum": [ + "active_directory", + "local_identity" + ], + "examples": [ + "active_directory" + ] + }, + "arc_resource_group": { + "type": "string", + "description": "Resource group for Azure Arc-connected resources", + "pattern": "^rg-[a-z0-9-]+$", + "examples": [ + "rg-azlc-iic-eus-arc" + ] + }, + "witness_type": { + "type": "string", + "description": "Cluster witness type for quorum", + "enum": [ + "cloud_witness", + "file_share_witness", + "disk_witness" + ], + "default": "cloud_witness" + }, + "cluster_ip": { + "type": "string", + "description": "Azure Local cluster IP address (management network)", + "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", + "examples": [ + "192.168.1.100" + ] + }, + "deployment_mode": { + "type": "string", + "description": "Deployment mode for Azure Local cluster", + "enum": [ + "validate_only", + "deploy" + ], + "default": "deploy" + } + }, + "additionalProperties": false + }, + "cluster_nodes": { + "type": "array", + "description": "Array of Azure Local cluster node configurations", + "minItems": 1, + "maxItems": 16, + "items": { + "type": "object", + "required": [ + "name", + "ip_address", + "idrac_ip" + ], + "properties": { + "name": { + "type": "string", + "description": "Node hostname (max 15 characters for NetBIOS)", + "maxLength": 15, + "examples": [ + "iic-01-node01" + ] + }, + "ip_address": { + "type": "string", + "description": "Management network IP address", + "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", + "examples": [ + "192.168.1.11" + ] + }, + "idrac_ip": { + "type": "string", + "description": "iDRAC/BMC IP address for out-of-band management (Dell hardware)", + "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", + "examples": [ + "192.168.2.11" + ] + }, + "mac_address": { + "type": "string", + "description": "Primary management NIC MAC address", + "pattern": "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$", + "examples": [ + "B8:CE:F6:4C:5D:6E" + ] + }, + "service_tag": { + "type": "string", + "description": "Dell service tag for hardware identification", + "examples": [ + "ABC1234" + ] + }, + "rack_position": { + "type": "string", + "description": "Physical rack location", + "examples": [ + "RAL-Rack01-U10" + ] + } + }, + "additionalProperties": false + } + }, + "network_intents": { + "type": "object", + "description": "Network intent configuration for Azure Local", + "required": [ + "management", + "storage", + "oob_management" + ], + "properties": { + "management": { + "type": "object", + "description": "Management network configuration", + "required": [ + "subnet", + "gateway", + "dns_servers" + ], + "properties": { + "subnet": { + "type": "string", + "description": "Management network subnet (CIDR)", + "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}\\/([0-9]|[1-2][0-9]|3[0-2])$", + "examples": [ + "192.168.1.0/24" + ] + }, + "gateway": { + "type": "string", + "description": "Management network default gateway", + "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", + "examples": [ + "192.168.1.1" + ] + }, + "vlan_id": { + "type": "integer", + "description": "Management network VLAN ID (0 for untagged)", + "minimum": 0, + "maximum": 4094, + "examples": [ + 0 + ] + }, + "dns_servers": { + "type": "array", + "description": "DNS server IP addresses", + "items": { + "type": "string", + "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$" + }, + "minItems": 1, + "examples": [ + [ + "192.168.1.10", + "192.168.1.11" + ] + ] + } + }, + "additionalProperties": false + }, + "storage": { + "type": "object", + "description": "Storage network configuration with RDMA", + "required": [ + "subnet_1", + "vlan_id_1" + ], + "properties": { + "subnet_1": { + "type": "string", + "description": "Storage network 1 subnet (CIDR)", + "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}\\/([0-9]|[1-2][0-9]|3[0-2])$", + "examples": [ + "10.71.1.0/24" + ] + }, + "subnet_2": { + "type": "string", + "description": "Storage network 2 subnet (CIDR) - for redundancy", + "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}\\/([0-9]|[1-2][0-9]|3[0-2])$", + "examples": [ + "10.71.2.0/24" + ] + }, + "vlan_id_1": { + "type": "integer", + "description": "Storage network 1 VLAN ID", + "minimum": 1, + "maximum": 4094, + "examples": [ + 711 + ] + }, + "vlan_id_2": { + "type": "integer", + "description": "Storage network 2 VLAN ID", + "minimum": 1, + "maximum": 4094, + "examples": [ + 712 + ] + }, + "rdma_enabled": { + "type": "boolean", + "description": "Whether RDMA is enabled for storage traffic (SMB Direct)", + "default": true + } + }, + "additionalProperties": false + }, + "compute": { + "type": "object", + "description": "VM/workload network configuration", + "properties": { + "subnet": { + "type": "string", + "description": "VM network subnet (CIDR)", + "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}\\/([0-9]|[1-2][0-9]|3[0-2])$", + "examples": [ + "10.250.10.0/24" + ] + }, + "gateway": { + "type": "string", + "description": "VM network default gateway", + "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", + "examples": [ + "10.250.10.1" + ] + }, + "vlan_id": { + "type": "integer", + "description": "VM network VLAN ID", + "minimum": 0, + "maximum": 4094, + "examples": [ + 0 + ] + } + }, + "additionalProperties": false + }, + "oob_management": { + "type": "object", + "description": "Out-of-band management network (iDRAC/BMC)", + "required": [ + "subnet" + ], + "properties": { + "subnet": { + "type": "string", + "description": "OOB management network subnet (CIDR)", + "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}\\/([0-9]|[1-2][0-9]|3[0-2])$", + "examples": [ + "192.168.2.0/24" + ] + }, + "gateway": { + "type": "string", + "description": "OOB management network gateway", + "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", + "examples": [ + "192.168.2.1" + ] + }, + "vlan_id": { + "type": "integer", + "description": "OOB management VLAN ID", + "minimum": 0, + "maximum": 4094, + "examples": [ + 0 + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "hardware": { + "type": "object", + "description": "Hardware configuration", + "properties": { + "vendor": { + "type": "string", + "description": "Hardware vendor", + "enum": [ + "Dell", + "HPE", + "Lenovo", + "DataON" + ], + "examples": [ + "Dell" + ] + }, + "model": { + "type": "string", + "description": "Hardware model", + "examples": [ + "AX-760" + ] + }, + "gpu_enabled": { + "type": "boolean", + "description": "Whether cluster includes GPU hardware", + "default": false + }, + "gpu_model": { + "type": "string", + "description": "GPU model if gpu_enabled is true", + "examples": [ + "NVIDIA L4" + ] + } + }, + "additionalProperties": false + }, + "onprem": { + "type": "object", + "description": "On-premises infrastructure configuration", + "required": [ + "domain_name", + "domain_netbios", + "dns_servers" + ], + "properties": { + "domain_name": { + "type": "string", + "description": "Active Directory domain name (FQDN)", + "pattern": "^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$", + "examples": [ + "improbability.cloud" + ] + }, + "domain_netbios": { + "type": "string", + "description": "NetBIOS name for the domain", + "maxLength": 15, + "pattern": "^[a-zA-Z][a-zA-Z0-9-]*$", + "examples": [ + "IMPROBABLE" + ] + }, + "dns_servers": { + "type": "array", + "description": "List of DNS server IP addresses", + "items": { + "type": "string", + "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$" + }, + "minItems": 1, + "examples": [ + [ + "192.168.1.10", + "192.168.1.11" + ] + ] + }, + "ntp_servers": { + "type": "array", + "description": "List of NTP server addresses (FQDN or IP)", + "items": { + "type": "string" + }, + "examples": [ + [ + "time.windows.com", + "pool.ntp.org" + ] + ] + }, + "ou_path": { + "type": "string", + "description": "Organizational Unit path for computer objects in AD", + "examples": [ + "OU=AzureLocal,OU=Servers,DC=improbability,DC=cloud" + ] + } + }, + "additionalProperties": false + }, + "credentials": { + "type": "object", + "description": "Credential references (values stored in Key Vault)", + "required": [ + "local_admin_username", + "local_admin_password_secret", + "domain_join_username", + "domain_join_password_secret" + ], + "properties": { + "local_admin_username": { + "type": "string", + "description": "Local administrator username for Azure Local nodes", + "examples": [ + "osfadmin" + ] + }, + "local_admin_password_secret": { + "type": "string", + "description": "Key Vault secret reference for local admin password", + "pattern": "^keyvault://[a-z0-9-]+/.+$", + "examples": [ + "keyvault://azl-platform-kv/local-admin-password" + ] + }, + "domain_join_username": { + "type": "string", + "description": "Username for domain join operations (UPN format)", + "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", + "examples": [ + "svc-azlocal@improbability.cloud" + ] + }, + "domain_join_password_secret": { + "type": "string", + "description": "Key Vault secret reference for domain join password", + "pattern": "^keyvault://[a-z0-9-]+/.+$", + "examples": [ + "keyvault://azl-platform-kv/domain-join-password" + ] + } + }, + "additionalProperties": false + }, + "environment": { + "type": "object", + "description": "Environment identity and metadata", + "required": [ + "name", + "type" + ], + "properties": { + "name": { + "type": "string", + "description": "Environment identifier (lowercase, no spaces)", + "enum": [ + "azurelocalcloud", + "iic-production", + "iic-reference", + "iic-poc", + "iic-productlabs" + ], + "examples": [ + "iic-production" + ] + }, + "type": { + "type": "string", + "description": "Environment type classification", + "enum": [ + "management", + "lab", + "demo", + "poc", + "production" + ], + "examples": [ + "lab" + ] + }, + "owner": { + "type": "string", + "description": "Email of the environment owner", + "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", + "examples": [ + "platform@improbability.cloud" + ] + }, + "description": { + "type": "string", + "description": "Brief description of the environment purpose", + "examples": [ + "2-node switchless Azure Local home lab for testing and development" + ] + } + }, + "additionalProperties": false + }, + "site": { + "type": "object", + "description": "Site information", + "required": [ + "code", + "name" + ], + "properties": { + "code": { + "type": "string", + "description": "Short site code (uppercase, 2-5 characters)", + "pattern": "^[A-Z]{2,5}$", + "maxLength": 5, + "examples": [ + "iic" + ] + }, + "name": { + "type": "string", + "description": "Full site name", + "examples": [ + "Infinite Improbability Corp HQ" + ] + }, + "location": { + "type": "string", + "description": "Physical location of the site", + "examples": [ + "Home Office" + ] + }, + "datacenter": { + "type": "string", + "description": "Datacenter code (for datacenter deployments)", + "examples": [ + "RAL" + ] + } + }, + "additionalProperties": false + }, + "tags": { + "type": "object", + "description": "Azure resource tags for governance", + "required": [ + "Environment", + "Project", + "ManagedBy", + "Owner" + ], + "properties": { + "Environment": { + "type": "string", + "description": "Environment name tag", + "examples": [ + "iic-production" + ] + }, + "Project": { + "type": "string", + "description": "Project name tag", + "examples": [ + "Azure Local Infrastructure" + ] + }, + "ManagedBy": { + "type": "string", + "description": "Management method tag", + "default": "Infrastructure as Code" + }, + "Owner": { + "type": "string", + "description": "Owner or team tag", + "examples": [ + "Product Technology Team" + ] + }, + "CostCenter": { + "type": "string", + "description": "Cost center for billing", + "examples": [ + "IT-IIC" + ] + } + }, + "additionalProperties": true + } + }, + "additionalProperties": false +} diff --git a/standards/variable-management/assets/master-registry.yaml b/standards/variable-management/assets/master-registry.yaml new file mode 100644 index 000000000..a45dcafbe --- /dev/null +++ b/standards/variable-management/assets/master-registry.yaml @@ -0,0 +1,11398 @@ +# ============================================================================= +# Master Variable Registry - Infinite Improbability Cloud Management Platform +# ============================================================================= +# This file defines ALL variable names, types, and descriptions used across +# the Infinite Improbability Cloud Management Platform (IIC-CMP) and all +# Azure Local environment repositories. Environments reference these definitions +# to ensure consistency. Actual VALUES are stored in each environment's +# infrastructure.yml file. +# +# Purpose: +# - Single source of truth for variable naming across all environments +# - Ensures variable X in iic-production is the same as variable X in Demos/POC/Labs +# - Self-documenting with descriptions, examples, and constraints +# - Enables validation via JSON Schema (schemas/infrastructure.schema.json) +# +# Usage: +# - When creating a new environment, reference variables from this registry +# - If a variable doesn't exist, add it here first with proper documentation +# - Values go in each environment's infrastructure.yml, not here +# +# Environments: +# - Azure Local Cloud (management hub tenant) +# - iic-production (2-node switchless home lab) +# - iic-reference (portable 2-node demo cluster with GPU) +# - iic-poc (nested virtualization proof-of-concept) +# - iic-productlabs (4-node datacenter lab) +# +# Statistics: +# - Total Variables: 970+ +# - Last Updated: 2026-02-01 +# - Version: 2.2.0 +# ============================================================================= + +# ============================================================================= +# CHANGE LOG +# ============================================================================= +# Version 2.2.0 (2026-02-01): +# - Added: B2B cross-tenant synchronization section (45+ variables) +# - Added: GitLab CI/CD configuration variables (4 variables) +# - Added: Arc Resource Bridge variables (3 variables) +# - Added: Cluster networking IP pool variables (2 variables) +# - Added: Network config variables (management_nic_name, jumbo_frames_mtu) +# - Added: Cluster config variables (cloud_witness_storage_account) +# - Added: Extended monitoring variables (Datadog, SCOM, WAC) +# - Source: Variable audit from 157 PowerShell scripts in azl-toolkit +# - Source: TODO.md missing variables tables +# +# Version 2.1.0 (2025-12-13): +# - Added: Hyper-V Host configuration variables (7 variables) +# - Added: Arc Resource Bridge for SCVMM integration (5 variables) +# - Added: Arc Hybrid VM management for Hyper-V/VMware VMs (8 variables) +# - Added: Windows Failover Cluster enhancements (6 variables) +# - Added: SCVMM connection enhancements (3 variables) +# - Added: Azure Virtual Desktop in Azure cloud (15 variables) +# - Added: Azure Virtual Desktop on Azure Local (8 variables) +# - Total: 52 new variables based on Microsoft Learn documentation +# - Research: Arc Resource Bridge, Arc-enabled servers, AVD on Azure Local +# - Critical: AVD host pools cannot mix Azure and Azure Local session hosts +# - Requirement: AVD on Azure Local requires 23H2+, Connected Machine agent +# +# Version 2.0.0 (2025-12-13): +# - BREAKING: Renamed all variables with consistent prefixes (aztenant_, mgtgrp_, sub_, etc.) +# - BREAKING: Removed duplicate variables (subnet_cidr, azl_storage_switchless_node_limit) +# - Added: Variable categories and subcategories (52 variables across 8 categories) +# - Added: Variable dependencies with depends_on field (10 variables) +# - Added: Triple-tier examples for arrays (minimal/standard/complete) - 5 major arrays +# - Added: allowedValues to tag variables (Environment, Project, ManagedBy) +# - Improved: All Key Vault patterns now enforce no consecutive hyphens +# - Improved: Enhanced descriptions for monitoring and DHCP variables (7 variables) +# - Improved: Cross-references between related sections (network_intents, logical_networks) +# - Fixed: Malformed array examples (def_alert_email_recipients) +# - Fixed: Indentation error in aztenant_iic-productlabs_primary_domain +# - Quality: 81% compliance baseline → 95%+ with systematic improvements +# +# Version 1.9.0 (2025-12-12): +# - Added: 17 new ARM template parameters for cluster deployment +# - Improved: network_intents array structure with full ARM compliance +# - Improved: logical_networks array with DHCP options and routes +# - Added: Comprehensive validation patterns and constraints +# +# Version 1.8.0 (2025-11-15): +# - Added: Azure Local cluster configuration variables +# - Added: Network intents and logical networks arrays +# - Improved: Security group and PIM variable definitions +# +# Version 1.5.0 (2025-10-20): +# - Added: Cross-tenant B2B variables +# - Added: Service Principal management variables +# - Improved: Key Vault variable organization +# +# Version 1.0.0 (2025-10-15): +# - Initial variable registry creation +# - Core sections: azure_tenants, networking, azure_local, monitoring +# - Foundation for multi-environment deployment consistency +# ============================================================================= + +# ============================================================================= +# INFRASTRUCTURE SCENARIO TAXONOMY +# ============================================================================= +# Defines which variable groups are required for each infrastructure deployment +# type. Used by Generate-Infrastructure.ps1 to filter variables based on flags. +# +# Usage: +# .\Generate-Infrastructure.ps1 -Environment iic-production -azure_local -avd_azure_local +# → Generates infrastructure.yml with only azure_local + avd_azure_local + shared variables +# +# No flags specified = complete registry (all 910+ variables) - backward compatible +# ============================================================================= + +infrastructure_scenarios: + # --------------------------------------------------------------------------- + # SHARED VARIABLE GROUPS (Always Included) + # --------------------------------------------------------------------------- + # These are automatically included with ANY infrastructure flag + shared_groups: + - azure_tenants # Tenant, subscription, service principals + - management_groups # Management group hierarchy + - subscriptions # Subscription configuration + - resource_groups # Resource groups, locations, tags + - hub_spoke_networking # VNets, subnets, NSGs, hub-spoke + - azure_vnets # Virtual network configuration + - vpn # VPN gateway configuration + - bastion # Azure Bastion configuration + - azure_firewall # Azure Firewall configuration + - monitoring # Log Analytics, Azure Monitor, Application Insights + - keyvault # Key Vault configurations (CMP platform and per-tenant) + - active_directory # AD DS domain integration + - policy # Azure Policy assignments + - defender # Defender for Cloud + - bcdr_backup_recovery # Recovery Services, backup policies + - storage # Storage accounts + - azure_vms # Management VMs (DCs, Jumpbox, WAC, Lighthouse, Syslog) + - accounts # Service accounts and credentials + - environment # Environment configuration + - site # Site configuration + - tags # Resource tagging standards + + # --------------------------------------------------------------------------- + # INFRASTRUCTURE TYPES + # --------------------------------------------------------------------------- + infrastructure_types: + azure_local: + description: "Azure Local (formerly Azure Stack HCI) cluster" + variable_groups: + - clusters + - cluster_nodes + - network_intents + - logical_networks + - vlans + - dhcp + - cluster_arm_deployment + conflicts_with: + - wsfc_s2d + - wsfc_san + estimated_variables: "250-350" + + wsfc_s2d: + description: "Windows Server Failover Clustering with Storage Spaces Direct" + variable_groups: + - clusters + - cluster_nodes + - network_devices + conflicts_with: + - azure_local + - wsfc_san + estimated_variables: "250-350" + + wsfc_san: + description: "Windows Server Failover Clustering with SAN storage" + variable_groups: + - clusters + - cluster_nodes + - network_devices + conflicts_with: + - azure_local + - wsfc_s2d + estimated_variables: "200-300" + + avd_azure: + description: "Azure Virtual Desktop in Azure cloud" + variable_groups: + - avd + conflicts_with: [] + requires: [] + estimated_variables: "50-100" + + avd_azure_local: + description: "Azure Virtual Desktop on Azure Local" + variable_groups: + - avd + conflicts_with: [] + requires: + - azure_local + estimated_variables: "50-100" + + scvmm: + description: "System Center Virtual Machine Manager integration" + variable_groups: + - scvmm + - arc_resource_bridge + conflicts_with: [] + requires: [] + estimated_variables: "30-50" + + arc_vms: + description: "Arc-enabled VMs (hybrid VM management)" + variable_groups: + - arc_hybrid_vms + conflicts_with: [] + requires: [] + estimated_variables: "20-40" + + hyperv_host: + description: "Standalone Hyper-V host (no cluster)" + variable_groups: [] + conflicts_with: + - azure_local + - wsfc_s2d + - wsfc_san + estimated_variables: "150-250" + note: "Only shared variable groups included" + + arc_scvmm: + description: "Arc Resource Bridge for SCVMM" + variable_groups: + - arc_resource_bridge + - scvmm + conflicts_with: [] + estimated_variables: "100-150" + + arc_hybrid_vms: + description: "Arc-enabled hybrid VMs (standalone Hyper-V, VMware, physical)" + variable_groups: + - arc_hybrid_vms + conflicts_with: [] + estimated_variables: "100-150" + note: "NOT for Azure Local cluster VMs - use azure_local flag for those" + + azure_only: + description: "Azure cloud infrastructure only (no on-premises clusters)" + variable_groups: [] + conflicts_with: + - azure_local + - wsfc_s2d + - wsfc_san + estimated_variables: "150-250" + note: "Only shared variable groups included" + +# ============================================================================= +# TABLE OF CONTENTS +# ============================================================================= +# AZURE INFRASTRUCTURE +# - Azure Tenants & Configuration (azure_tenants) +# - Tenant Root Management Group (tenant_root) +# - Management Groups (management_groups) +# - Subscriptions (subscriptions) +# - Resource Groups (resource_groups) +# +# CLOUD NETWORKING +# - Hub/Spoke Networking (hub_spoke_networking) +# - Azure VNets (azure_vnets) +# - VPN (vpn) +# - Azure Bastion (bastion) +# - Azure Firewall (firewall) +# +# ON-PREMISES NETWORKING +# - VLANs (vlans) +# - DHCP (dhcp) +# - Network Devices (network_devices) +# +# AZURE VIRTUAL MACHINES +# - Domain Controllers (dc1, dc2, domain_controllers) +# - Jumpbox (jumpbox) +# - Windows Admin Center (wac) +# - Lighthouse Server (lighthouse) +# - Syslog/SNMP Server (syslog) +# - Shared VM Settings (vm_shared) +# +# CLUSTER NODES (GENERIC - SHARED INFRASTRUCTURE) +# - Physical Node Specifications (cluster_nodes) +# - Hardware, Network Adapters, Storage (cluster_nodes) +# - Used by BOTH Azure Local and WSFC (cluster_nodes) +# +# AZURE LOCAL CLUSTER +# - Cluster Configuration (azure_local) +# - Cluster ARM Deployment (cluster_arm_deployment) +# - Network Intents (network_intents) +# - Logical Networks (logical_networks) +# - Cluster Storage (azurelocal_cluster_storage) +# - SCVMM Integration (scvmm) +# +# HYPER-V HOST +# - Virtual Switch Configuration (hyperv) +# - Live Migration Settings (hyperv) +# - Advanced Host Settings (hyperv) +# +# AZURE ARC +# - Arc Resource Bridge for SCVMM (arc_resource_bridge) +# - Arc Hybrid VM Management (arc_hybrid_vms) +# +# WINDOWS SERVER FAILOVER CLUSTER (WSFC) +# - Cluster Configuration (wsfc) +# - OS Version Selection (wsfc) +# - Storage Architecture (wsfc_s2d, wsfc_san) +# - Network Configuration (wsfc) +# - CSV Settings (wsfc) +# - Quorum Configuration (wsfc) +# - Cluster Roles (wsfc) +# +# AZURE VIRTUAL DESKTOP +# - AVD in Azure Cloud (avd_azure) +# - AVD on Azure Local (avd_azure_local) +# +# IDENTITY & SECURITY +# - Accounts (accounts) +# - On-Premises (onprem) +# - Credentials (credentials) +# - Active Directory (active_directory) +# - Service Principals & PIM (service_principals, pim) +# - Security & Compliance (security) +# - B2B Cross-Tenant Synchronization (b2b_configuration) **NEW v2.2.0** +# +# KEY VAULT +# - CMP Platform Key Vault (cmp_platform_keyvault) +# - Platform Key Vault (platform_keyvault) +# - Azure Local Deployment Key Vault (azurelocal_deployment_keyvault) +# +# MONITORING & OPERATIONS +# - Environment (environment) +# - Site (site) +# - Tags (tags) +# - Monitoring & Log Analytics (monitoring) +# - Extended Monitoring (monitoring_extended) **NEW v2.2.0** +# - Data Collection Rules (data_collection) +# - Azure Policy (policy) +# - Azure Update Manager (maintenance) +# - Backup & Disaster Recovery (backup) +# - GitLab CI/CD (gitlab) **NEW v2.2.0** +# +# NETWORKING (ADDITIONAL) +# - Network Config (network_config) **NEW v2.2.0** +# - Cluster Networking (cluster_networking) **NEW v2.2.0** +# +# CLUSTER CONFIGURATION (ADDITIONAL) +# - Cluster Config (cluster_config) **NEW v2.2.0** +# +# AZURE ARC (ADDITIONAL) +# - Arc Resource Bridge (arc_resource_bridge) **NEW v2.2.0** +# +# STORAGE & RESOURCES +# - Storage (storage) +# - Microsoft Defender for Cloud & Security (defender) +# +# HARDWARE +# - Hardware (hardware) +# +# MISCELLANEOUS +# - Alert, Automation, Azure, Break, Breakglass, Cloud, Cluster +# - Cost, CSV, DCR, Defender, Deployment, Metrics, Patch +# - Platform, Recovery, Service, Soft, SPN, TF +# ============================================================================= + +# ============================================================================= +# AZURE TENANTS & CONFIGURATION +# ============================================================================= + +azure_tenants: + # Primary Tenant - Azure Local Cloud + aztenant_azurelocal_id: + category: "identity" + subcategory: "tenant" + type: string + description: "Azure AD/Entra ID Tenant ID (GUID)" + required: true + format: uuid + example: "00000000-aaaa-bbbb-cccc-111111111111" + + aztenant_azurelocal_name: + category: "identity" + subcategory: "tenant" + type: string + description: "Azure tenant display name" + required: true + example: "Azure Local Cloud" + + aztenant_azurelocal_domain: + category: "identity" + subcategory: "tenant" + type: string + description: "Azure tenant domain" + required: true + pattern: "^[a-z0-9-]+\\.onmicrosoft\\.com$" + example: "azurelocalcloud.onmicrosoft.com" + + aztenant_azurelocal_primary_domain: + type: string + description: "Primary custom domain" + required: true + example: "azurelocal.cloud" + + aztenant_region: + category: "identity" + subcategory: "region" + type: string + description: "Azure region code" + required: true + example: "eastus" + + aztenant_environment: + type: string + description: "Azure cloud environment" + required: false + allowedValues: + - AzureCloud + - AzureUSGovernment + - AzureChinaCloud + default: "AzureCloud" + + aztenant_location: + category: "identity" + subcategory: "region" + type: string + description: "Azure region (full name)" + required: true + allowedValues: + - eastus + - eastus2 + - westus + - westus2 + - centralus + - northeurope + - westeurope + example: "eastus" + + aztenant_location_short: + type: string + description: "Azure region (short form)" + required: true + pattern: "^[a-z]{2,5}[0-9]?$" + example: "eus" + + # Tenant: Infinite Improbability Corp + aztenant_iic_production_id: + type: string + description: "Infinite Improbability Corp Tenant ID" + required: false + format: uuid + example: "c9257f80-1ad6-40b4-8f53-f3d58b75d58b" + + aztenant_iic_production_domain: + type: string + description: "Infinite Improbability Corp tenant domain" + required: false + example: "iicproduction.onmicrosoft.com" + + aztenant_iic_production_primary_domain: + type: string + description: "Infinite Improbability Corp primary domain" + required: false + example: "improbability.cloud" + + # Tenant: IIC Demos + aztenant_iic-reference_id: + type: string + description: "IIC Demos Tenant ID" + required: false + format: uuid + example: "12345678-1234-1234-1234-123456789012" + + aztenant_iic-reference_domain: + type: string + description: "IIC Demos tenant domain" + required: false + example: "iic-reference.onmicrosoft.com" + + aztenant_iic-reference_primary_domain: + type: string + description: "IIC Demos primary domain" + required: false + example: "iic-reference.cloud" + + # Tenant: IIC POC + aztenant_iic-poc_id: + type: string + description: "IIC POC Tenant ID" + required: false + format: uuid + example: "12345678-1234-1234-1234-123456789013" + + aztenant_iic-poc_domain: + type: string + description: "IIC POC tenant domain" + required: false + example: "iic-poc.onmicrosoft.com" + + aztenant_iic-poc_primary_domain: + type: string + description: "IIC POC primary domain" + required: false + example: "iic-poc.cloud" + + # Tenant: IIC Product Labs + aztenant_iic-productlabs_id: + type: string + description: "IIC Product Labs Tenant ID" + required: false + format: uuid + example: "12345678-1234-1234-1234-123456789014" + + aztenant_iic-productlabs_domain: + type: string + description: "IIC Product Labs tenant domain" + required: false + example: "iic-productlabs.onmicrosoft.com" + + aztenant_iic-productlabs_primary_domain: + type: string + description: "IIC Product Labs primary domain" + required: false + example: "iic-productlabs.cloud" + +# ============================================================================= +# MANAGEMENT GROUPS +# ============================================================================= + +management_groups: + # ============================================================================= + # TENANT ROOT MANAGEMENT GROUP + # ============================================================================= + + tenant_root: + mgtgrp_tenant_root_name: + type: string + description: "Tenant root management group name (top-level group for entire tenant)" + required: false + example: "Tenant Root Group" + + # ============================================================================= + # CUSTOM MANAGEMENT GROUPS + # ============================================================================= + + custom: + # Root Management Group + mgtgrp_root_name: + category: "identity" + subcategory: "management-group" + type: string + description: "Root management group name (varies by environment)" + required: true + pattern: "^mg-[a-z0-9-]+$" + example: "mg-azlc-iic" + + mgtgrp_root_display_name: + type: string + description: "Root management group display name (varies by environment)" + required: true + example: "Azure Local Cloud Management" + + mgtgrp_platform_name: + type: string + description: "Platform management group name (varies by environment)" + required: false + pattern: "^platform-[a-z0-9-]+$" + example: "platform-connectivity" + + mgtgrp_platform_display_name: + type: string + description: "Platform management group display name (varies by environment)" + required: false + example: "Platform Connectivity" + + # Platform Management Groups + mgtgrp_identity_name: + type: string + description: "Identity platform group name (varies by environment)" + required: false + pattern: "^platform-[a-z0-9-]+$" + example: "platform-identity" + + mgtgrp_connectivity_name: + type: string + description: "Connectivity platform group name (varies by environment)" + required: false + pattern: "^platform-[a-z0-9-]+$" + example: "platform-connectivity" + + mgtgrp_management_name: + type: string + description: "Management platform group name (varies by environment)" + required: false + pattern: "^platform-[a-z0-9-]+$" + example: "platform-management" + + mgtgrp_platform_identity_name: + type: string + description: "Platform Identity management group name" + required: false + pattern: "^platform-[a-z0-9-]+$" + example: "platform-identity" + + mgtgrp_platform_security_name: + type: string + description: "Platform Security management group name" + required: false + pattern: "^platform-[a-z0-9-]+$" + example: "platform-security" + + mgtgrp_platform_connectivity_name: + type: string + description: "Platform Connectivity management group name" + required: false + pattern: "^platform-[a-z0-9-]+$" + example: "platform-connectivity" + + mgtgrp_platform_management_name: + type: string + description: "Platform Management management group name" + required: false + pattern: "^platform-[a-z0-9-]+$" + example: "platform-management" + + mgtgrp_security_name: + type: string + description: "Security platform group name (varies by environment)" + required: false + pattern: "^platform-[a-z0-9-]+$" + example: "platform-security" + + # Landing Zones + mgtgrp_landing_zones_name: + type: string + description: "Landing zones management group name (varies by environment)" + required: false + pattern: "^lz-[a-z0-9-]+$" + example: "lz-azurelocalcloud" + + # Customer Branch Management Groups + mgtgrp_customer_root_name: + type: string + description: "Customer environment root management group name" + required: false + example: "mg-cmp-labs-root" + + mgtgrp_customer_platform_connectivity_name: + type: string + description: "Customer platform connectivity management group name" + required: false + example: "platform-connectivity-labs" + + mgtgrp_customer_platform_identity_name: + type: string + description: "Customer platform identity management group name" + required: false + example: "platform-identity-labs" + + mgtgrp_customer_platform_management_name: + type: string + description: "Customer platform management management group name" + required: false + example: "platform-management-labs" + + mgtgrp_customer_platform_security_name: + type: string + description: "Customer platform security management group name" + required: false + example: "platform-security-labs" + + mgtgrp_customer_landing_zones_name: + type: string + description: "Customer landing zones management group name" + required: false + example: "landing-zones-labs" + + mgtgrp_customer_landing_zone_prod_name: + type: string + description: "Customer production landing zone management group name" + required: false + example: "lz-labs-demo-prod" + + mgtgrp_customer_sandbox_name: + type: string + description: "Customer sandbox management group name" + required: false + example: "sandbox-labs" + + mgtgrp_customer_decommissioned_name: + type: string + description: "Customer decommissioned resources management group name" + required: false + example: "decommissioned-labs" + + # Sandbox and Decommissioned + mgtgrp_sandbox_name: + type: string + description: "Sandbox management group name" + required: false + pattern: "^sandbox(-[a-z0-9-]+)?$" + example: "sandbox" + + mgtgrp_decommissioned_name: + type: string + description: "Decommissioned management group name" + required: false + pattern: "^decommissioned-[a-z0-9-]+$" + example: "decommissioned-azlc" + +# ============================================================================= +# SUBSCRIPTIONS +# ============================================================================= + +subscriptions: + sub_bootstrap_id: + category: "identity" + subcategory: "subscription" + type: string + description: "Bootstrap subscription ID" + required: false + format: uuid + example: "00000000-1111-2222-3333-444444444444" + + sub_bootstrap_name: + type: string + description: "Bootstrap subscription name" + required: false + example: "Azure Local Cloud Bootstrap" + + sub_connectivity_id: + category: "identity" + subcategory: "subscription" + type: string + description: "Connectivity subscription ID" + required: false + format: uuid + example: "12345678-1234-1234-1234-123456789015" + + sub_connectivity_name: + type: string + description: "Connectivity subscription name" + required: false + example: "Azure Local Cloud Connectivity" + + sub_management_id: + category: "identity" + subcategory: "subscription" + type: string + description: "Management subscription ID" + required: false + format: uuid + example: "12345678-1234-1234-1234-123456789016" + + sub_security_id: + type: string + description: "Security subscription ID" + required: false + format: uuid + example: "12345678-1234-1234-1234-123456789017" + + sub_security_name: + type: string + description: "Security subscription name" + required: false + example: "Azure Local Cloud Security" + + sub_identity_id: + category: "identity" + subcategory: "subscription" + type: string + description: "Identity subscription ID" + required: false + format: uuid + example: "12345678-1234-1234-1234-123456789018" + + sub_identity_name: + type: string + description: "Identity subscription name" + required: false + example: "Azure Local Cloud Identity" + + sub_azure_local_id: + category: "identity" + subcategory: "subscription" + type: string + description: "Azure Local subscription ID" + required: false + format: uuid + example: "00000000-1111-2222-3333-444444444444" + + sub_azure_local_name: + type: string + description: "Azure Local subscription name" + required: false + example: "Azure Local Cloud" + + sub_workloads_id: + type: string + description: "Workloads subscription ID" + required: false + format: uuid + example: "12345678-1234-1234-1234-123456789019" + + sub_workloads_name: + type: string + description: "Workloads subscription name" + required: false + example: "Azure Local Cloud Workloads" + +# ============================================================================= +# RESOURCE GROUPS +# ============================================================================= + +resource_groups: + # Connectivity Function - Network Infrastructure + rg_connectivity_hub: + type: string + description: "Connectivity hub resource group - hub VNet, gateways, core networking" + required: false + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(connectivity)-hub$" + example: "rg-azlc-iic-eus-connectivity-hub" + + rg_connectivity_dns: + type: string + description: "Connectivity DNS resource group - DNS zones, private DNS" + required: false + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(connectivity)-dns$" + example: "rg-azlc-iic-eus-connectivity-dns" + + rg_connectivity_bastion: + type: string + description: "Connectivity Bastion resource group - Azure Bastion hosts" + required: false + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(connectivity)-bastion$" + example: "rg-azlc-iic-eus-connectivity-bastion" + + rg_connectivity_arcgw: + type: string + description: "Connectivity Arc Gateway resource group - Arc Gateway infrastructure" + required: false + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(connectivity)-arcgw$" + example: "rg-azlc-iic-eus-connectivity-arcgw" + + # Identity Function - Security & Identity Services + rg_identity_mgmt: + type: string + description: "Identity management resource group - Key Vaults, managed identities, security services" + required: false + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(identity)-mgmt$" + example: "rg-azlc-iic-eus-identity-mgmt" + + # Management Function - Operations & Governance + rg_management_monitoring: + type: string + description: "Management monitoring resource group - Log Analytics, Application Insights, alerts" + required: false + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(management)-monitoring$" + example: "rg-azlc-iic-eus-management-monitoring" + + rg_management_backup: + type: string + description: "Management backup resource group - Recovery vaults, backup policies" + required: false + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(management)-backup$" + example: "rg-azlc-iic-eus-management-backup" + + rg_management_automation: + type: string + description: "Management automation resource group - Automation accounts, runbooks" + required: false + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(management)-automation$" + example: "rg-azlc-iic-eus-management-automation" + + # Workload Function - Application Resources + rg_workload_compute: + type: string + description: "Workload compute resource group - VMs, VM scale sets, compute resources" + required: false + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(workload)-compute$" + example: "rg-azlc-iic-eus-workload-compute" + + rg_workload_data: + type: string + description: "Workload data resource group - Databases, data services" + required: false + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(workload)-data$" + example: "rg-azlc-iic-eus-workload-data" + + rg_workload_web: + type: string + description: "Workload web resource group - App Services, web applications" + required: false + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(workload)-web$" + example: "rg-azlc-iic-eus-workload-web" + + rg_workload_storage: + type: string + description: "Workload storage resource group - Storage accounts for application data" + required: false + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(workload)-storage$" + example: "rg-azlc-iic-eus-workload-storage" + + # Azure Local Function - HCI/Azure Stack HCI Specific + rg_azurelocal_cluster: + type: string + description: "Azure Local cluster resource group - Azure Local cluster resources" + required: false + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(azurelocal)-cluster$" + example: "rg-azlc-iic-eus-azurelocal-cluster" + + rg_azurelocal_arc: + type: string + description: "Azure Local Arc resource group - Arc-enabled infrastructure (SCVMM, Arc VMs)" + required: false + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(azurelocal)-arc$" + example: "rg-azlc-iic-eus-azurelocal-arc" + + rg_azurelocal_network: + type: string + description: "Azure Local network resource group - Logical networks, network intents" + required: false + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(azurelocal)-network$" + example: "rg-azlc-iic-eus-azurelocal-network" + +# ============================================================================= +# CLOUD NETWORKING - HUB/SPOKE +# ============================================================================= + +hub_spoke_networking: + hub_vnet_name: + category: "networking" + subcategory: "hub-spoke" + type: string + description: "Hub virtual network name" + required: false + pattern: "^vnet-[a-z0-9-]+$" + example: "vnet-azlc-iic-eus-connectivity-hub" + + hub_vnet_address_space: + category: "networking" + subcategory: "hub-spoke" + type: array + description: "Hub VNet address space (CIDR)" + required: false + items: + type: string + format: cidr + example: + - "192.168.0.0/16" + + hub_resource_group: + category: "networking" + subcategory: "hub-spoke" + type: string + description: "Hub resource group name" + required: false + pattern: "^rg-[a-z0-9-]+$" + example: "rg-azlc-iic-eus-connectivity-hub" + + hub_subscription: + category: "networking" + subcategory: "hub-spoke" + type: string + description: "Hub VNet subscription ID" + required: false + format: uuid + example: "00000000-1111-2222-3333-444444444444" + + hub_location: + type: string + description: "Hub VNet location" + required: false + example: "East US" + + hub_enable_ddos_protection: + type: boolean + description: "Enable DDoS protection on hub VNet" + required: false + default: false + + hub_ddos_protection_plan_id: + type: string + description: "DDoS protection plan resource ID for hub" + required: false + depends_on: + - variable: "hub_enable_ddos_protection" + condition: "equals: true" + message: "DDoS protection plan ID required when DDoS protection is enabled" + format: azure_resource_id + example: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-connectivity-hub/providers/Microsoft.Network/ddosProtectionPlans/ddos-plan-azlc" + + hub_tags: + type: object + description: "Tags for hub VNet resources" + required: false + example: + environment: "prod" + function: "connectivity-hub" + cost_center: "IT-Infrastructure" + + hub_gateway_subnet_name: + type: string + description: "Gateway subnet name" + required: false + default: "GatewaySubnet" + + hub_gateway_subnet_address: + type: string + description: "Gateway subnet address prefix (CIDR)" + required: false + format: cidr + example: "192.168.0.0/24" + + hub_bastion_subnet_name: + type: string + description: "Bastion subnet name" + required: false + default: "AzureBastionSubnet" + + hub_bastion_subnet_address: + type: string + description: "Bastion subnet address prefix (CIDR)" + required: false + format: cidr + example: "192.168.1.0/26" + + hub_firewall_subnet_name: + type: string + description: "Firewall subnet name" + required: false + default: "AzureFirewallSubnet" + + hub_firewall_subnet_address: + type: string + description: "Firewall subnet address prefix (CIDR)" + required: false + format: cidr + example: "192.168.2.0/26" + + hub_management_subnet_name: + type: string + description: "Management subnet name" + required: false + pattern: "^snet-[a-z0-9-]+$" + example: "snet-azlc-iic-eus-connectivity-mgmt" + + hub_privateendpoint_subnet_name: + type: string + description: "Private endpoint subnet name" + required: false + pattern: "^snet-[a-z0-9-]+$" + example: "snet-azlc-iic-eus-connectivity-pep" + + hub_privateendpoint_subnet_address: + type: string + description: "Private endpoint subnet address prefix (CIDR)" + required: false + format: cidr + example: "192.168.3.0/24" + + hub_monitoring_subnet_name: + type: string + description: "Monitoring subnet name" + required: false + pattern: "^snet-[a-z0-9-]+$" + example: "snet-azlc-iic-eus-connectivity-monitoring" + + hub_monitoring_subnet_address: + type: string + description: "Monitoring subnet address prefix (CIDR)" + required: false + format: cidr + example: "192.168.4.0/24" + + hub_route_server_subnet_name: + type: string + description: "Azure Route Server subnet name (must be RouteServerSubnet)" + required: false + default: "RouteServerSubnet" + + hub_route_server_subnet_address: + type: string + description: "Route Server subnet address prefix (CIDR, minimum /27)" + required: false + format: cidr + example: "192.168.5.0/27" + + hub_enable_route_server: + type: boolean + description: "Enable Azure Route Server in hub for BGP route propagation" + required: false + default: false + + hub_route_server_name: + type: string + description: "Azure Route Server name" + required: false + depends_on: + - variable: "hub_enable_route_server" + condition: "equals: true" + message: "Route Server name required when Route Server is enabled" + pattern: "^routeserver-[a-z0-9-]+$" + example: "routeserver-azlc-iic-eus" + + hub_route_server_allow_branch_to_branch: + type: boolean + description: "Allow branch-to-branch traffic through Route Server" + required: false + depends_on: + - variable: "hub_enable_route_server" + condition: "equals: true" + message: "Branch-to-branch setting only applies when Route Server is enabled" + default: true + + hub_nva_subnet_name: + type: string + description: "Network Virtual Appliance subnet name" + required: false + pattern: "^snet-[a-z0-9-]+$" + example: "snet-azlc-iic-eus-connectivity-nva" + + hub_nva_subnet_address: + type: string + description: "NVA subnet address prefix (CIDR)" + required: false + format: cidr + example: "192.168.6.0/24" + + hub_subnets: + type: array + description: "Complete array of hub subnets with configurations" + required: false + items: + type: object + properties: + name: + type: string + address_prefix: + type: string + format: cidr + nsg: + type: string + route_table: + type: string + service_endpoints: + type: array + delegation: + type: string + example: + minimal: + # Simplest configuration - gateway and bastion only + - name: "GatewaySubnet" + address_prefix: "192.168.0.0/24" + - name: "AzureBastionSubnet" + address_prefix: "192.168.1.0/26" + + standard: + # Production hub with firewall and management subnet + - name: "GatewaySubnet" + address_prefix: "192.168.0.0/24" + - name: "AzureBastionSubnet" + address_prefix: "192.168.1.0/26" + - name: "AzureFirewallSubnet" + address_prefix: "192.168.2.0/26" + - name: "snet-azlc-iic-eus-connectivity-mgmt" + address_prefix: "192.168.3.0/24" + nsg: "nsg-azlc-iic-eus-connectivity-mgmt" + route_table: "rt-azlc-iic-eus-mgmt" + + complete: + # Enterprise hub with all services and delegations + - name: "GatewaySubnet" + address_prefix: "192.168.0.0/24" + - name: "AzureBastionSubnet" + address_prefix: "192.168.1.0/26" + - name: "AzureFirewallSubnet" + address_prefix: "192.168.2.0/26" + - name: "snet-azlc-iic-eus-connectivity-mgmt" + address_prefix: "192.168.3.0/24" + nsg: "nsg-azlc-iic-eus-connectivity-mgmt" + route_table: "rt-azlc-iic-eus-mgmt" + service_endpoints: + - "Microsoft.Storage" + - "Microsoft.KeyVault" + - name: "snet-azlc-iic-eus-connectivity-pep" + address_prefix: "192.168.4.0/24" + nsg: "nsg-azlc-iic-eus-connectivity-pep" + - name: "snet-azlc-iic-eus-connectivity-apim" + address_prefix: "192.168.5.0/24" + delegation: "Microsoft.ApiManagement/service" + + hub_vnet_additional_subnets: + type: array + description: "Additional hub subnets" + required: false + items: + type: object + properties: + name: + type: string + description: "Subnet name" + address_prefix: + type: string + format: cidr + description: "Subnet address prefix" + example: + - name: "snet-custom" + address_prefix: "192.168.5.0/24" + + hub_enable_virtual_wan: + type: boolean + description: "Use Azure Virtual WAN instead of traditional hub-spoke" + required: false + default: false + + hub_virtual_wan_name: + type: string + description: "Azure Virtual WAN name" + required: false + pattern: "^vwan-[a-z0-9-]+$" + example: "vwan-azlc-iic" + + hub_virtual_wan_type: + type: string + description: "Virtual WAN type" + required: false + allowedValues: + - Basic + - Standard + default: "Standard" + + hub_virtual_hub_name: + type: string + description: "Virtual WAN hub name" + required: false + pattern: "^vhub-[a-z0-9-]+$" + example: "vhub-azlc-iic-eus" + + hub_virtual_hub_address_prefix: + type: string + description: "Virtual hub address prefix (CIDR, minimum /24)" + required: false + format: cidr + example: "192.168.255.0/24" + + hub_virtual_hub_sku: + type: string + description: "Virtual hub SKU" + required: false + allowedValues: + - Basic + - Standard + default: "Standard" + + hub_enable_routing_intent: + type: boolean + description: "Enable routing intent for Virtual WAN hub" + required: false + default: false + + hub_routing_preference: + type: string + description: "Hub routing preference" + required: false + allowedValues: + - ExpressRoute + - VpnGateway + - ASPath + default: "ExpressRoute" + + hub_spoke_vnets: + type: array + description: "Spoke virtual networks" + required: false + items: + type: object + properties: + name: + type: string + pattern: "^vnet-[a-z0-9-]+$" + address_space: + type: string + format: cidr + subscription: + type: string + format: uuid + resource_group: + type: string + pattern: "^rg-[a-z0-9-]+$" + example: + - name: "vnet-spoke1" + address_space: "10.1.0.0/16" + subscription: "sub-id" + resource_group: "rg-spoke1" + + hub_spoke_vnet_address_range_base: + type: string + description: "Base address range for spoke VNets (CIDR)" + required: false + format: cidr + example: "10.0.0.0/8" + + hub_spoke_subnet_address_range_base: + type: string + description: "Base address range for spoke subnets (CIDR)" + required: false + format: cidr + example: "10.0.0.0/24" + + hub_spoke_enable_vpn_gateway: + type: boolean + description: "Enable VPN gateway in spoke VNets (branch connectivity)" + required: false + default: false + + hub_spoke_use_hub_gateway: + type: boolean + description: "Spoke VNets use hub gateway for external connectivity" + required: false + default: true + + hub_spoke_route_table_name: + type: string + description: "Default route table name for spoke subnets" + required: false + pattern: "^rt-[a-z0-9-]+$" + example: "rt-azlc-iic-eus-spoke-default" + + hub_spoke_nsg_name_prefix: + type: string + description: "NSG name prefix for spoke subnets" + required: false + pattern: "^nsg-[a-z0-9-]+$" + example: "nsg-azlc-iic-eus-spoke" + + hub_spoke_to_spoke_connectivity: + type: string + description: "Spoke-to-spoke connectivity method" + required: false + allowedValues: + - None + - HubFirewall + - HubNVA + - RouteServer + - VirtualWAN + default: "HubFirewall" + + hub_spoke_default_route_next_hop: + type: string + description: "Next hop for default route (0.0.0.0/0) from spokes" + required: false + example: "192.168.2.4" + + hub_spoke_default_route_next_hop_type: + type: string + description: "Next hop type for spoke default route" + required: false + allowedValues: + - VirtualAppliance + - VirtualNetworkGateway + - Internet + - None + default: "VirtualAppliance" + + vnet_peering_configurations: + type: array + description: "VNet peering configurations" + required: false + items: + type: object + properties: + name: + type: string + remote_vnet: + type: string + allow_forwarded_traffic: + type: boolean + allow_gateway_transit: + type: boolean + use_remote_gateways: + type: boolean + example: + - name: "peer-hub-to-spoke1" + remote_vnet: "vnet-spoke1" + allow_forwarded_traffic: true + allow_gateway_transit: true + use_remote_gateways: false + + enable_transit_connectivity: + type: boolean + description: "Enable transit connectivity between spokes through hub" + required: false + default: true + + transit_nva_name: + type: string + description: "Network Virtual Appliance name for transit" + required: false + pattern: "^vm-[a-z0-9-]+$" + example: "vm-azlc-iic-eus-nva001" + + transit_nva_private_ip: + type: string + description: "NVA private IP for routing" + required: false + format: ipv4 + example: "192.168.6.4" + + transit_nva_bgp_asn: + type: integer + description: "NVA BGP ASN for route propagation" + required: false + minimum: 64512 + maximum: 65534 + example: 65001 + + enable_forced_tunneling: + type: boolean + description: "Force all internet traffic through hub (0.0.0.0/0 to hub)" + required: false + default: true + + hub_internet_egress_type: + type: string + description: "Method for internet egress from hub" + required: false + allowedValues: + - AzureFirewall + - NVA + - NATGateway + - Direct + default: "AzureFirewall" + + enable_vnet_injection: + type: boolean + description: "Enable VNet injection for Azure services (PaaS integration)" + required: false + default: true + + vnet_injection_subnets: + type: array + description: "Subnets configured for VNet injection" + required: false + items: + type: object + properties: + name: + type: string + service: + type: string + delegation: + type: string + example: + - name: "snet-azlc-iic-eus-connectivity-apim" + service: "API Management" + delegation: "Microsoft.ApiManagement/service" + + nsg_management_name: + category: "networking" + subcategory: "security" + type: string + description: "Management NSG name" + required: false + pattern: "^nsg-[a-z0-9-]+$" + example: "nsg-azlc-iic-eus-connectivity-mgmt" + + nsg_privateendpoint_name: + category: "networking" + subcategory: "security" + type: string + description: "Private endpoint NSG name" + required: false + pattern: "^nsg-[a-z0-9-]+$" + example: "nsg-azlc-iic-eus-connectivity-pep" + + dns_zone_resource_group: + category: "networking" + subcategory: "dns" + type: string + description: "DNS zone resource group" + required: false + pattern: "^rg-[a-z0-9-]+$" + example: "rg-azlc-iic-eus-connectivity-dns" + + route_tables: + type: array + description: "Route tables for UDRs" + required: false + items: + type: object + properties: + name: + type: string + routes: + type: array + disable_bgp_route_propagation: + type: boolean + associated_subnets: + type: array + example: + minimal: + # Simplest - single default route + - name: "rt-hub" + routes: + - name: "default-route" + address_prefix: "0.0.0.0/0" + next_hop_type: "Internet" + + standard: + # Production route table with firewall next hop + - name: "rt-hub" + disable_bgp_route_propagation: false + routes: + - name: "to-firewall" + address_prefix: "0.0.0.0/0" + next_hop_type: "VirtualAppliance" + next_hop_ip: "192.168.2.4" + associated_subnets: + - "snet-azlc-iic-eus-connectivity-mgmt" + + complete: + # Enterprise with multiple routes and subnet associations + - name: "rt-hub" + disable_bgp_route_propagation: false + routes: + - name: "default-route" + address_prefix: "0.0.0.0/0" + next_hop_type: "VirtualAppliance" + next_hop_ip: "192.168.2.4" + - name: "to-spoke1" + address_prefix: "10.1.0.0/16" + next_hop_type: "VirtualNetworkGateway" + - name: "to-onprem" + address_prefix: "10.0.0.0/8" + next_hop_type: "VirtualAppliance" + next_hop_ip: "192.168.2.4" + associated_subnets: + - "snet-azlc-iic-eus-connectivity-mgmt" + - "snet-azlc-iic-eus-connectivity-pep" + + default_route_table_name: + type: string + description: "Default route table for hub-spoke topology" + required: false + pattern: "^rt-[a-z0-9-]+$" + example: "rt-azlc-iic-eus-default" + + disable_bgp_route_propagation: + type: boolean + description: "Disable BGP route propagation on route tables" + required: false + default: false + + network_security_groups: + type: array + description: "Network security groups for subnets" + required: false + items: + type: object + properties: + name: + type: string + rules: + type: array + example: + - name: "nsg-web" + rules: + - name: "allow-http" + priority: 100 + direction: "Inbound" + access: "Allow" + + private_dns_zones: + type: array + description: "Private DNS zones" + required: false + items: + type: string + example: + - "privatelink.blob.core.windows.net" + - "privatelink.vaultcore.azure.net" + + private_dns_zone_links: + type: array + description: "VNet links for private DNS zones" + required: false + items: + type: object + properties: + zone_name: + type: string + vnet_name: + type: string + registration_enabled: + type: boolean + example: + minimal: + # Single zone link to hub VNet + - zone_name: "privatelink.vaultcore.azure.net" + vnet_name: "vnet-azlc-iic-eus-connectivity-hub" + registration_enabled: false + + standard: + # Multiple service zones linked to hub + - zone_name: "privatelink.blob.core.windows.net" + vnet_name: "vnet-azlc-iic-eus-connectivity-hub" + registration_enabled: false + - zone_name: "privatelink.vaultcore.azure.net" + vnet_name: "vnet-azlc-iic-eus-connectivity-hub" + registration_enabled: false + - zone_name: "privatelink.database.windows.net" + vnet_name: "vnet-azlc-iic-eus-connectivity-hub" + registration_enabled: false + + complete: + # All Azure service zones with hub and spoke links + - zone_name: "privatelink.blob.core.windows.net" + vnet_name: "vnet-azlc-iic-eus-connectivity-hub" + registration_enabled: false + - zone_name: "privatelink.vaultcore.azure.net" + vnet_name: "vnet-azlc-iic-eus-connectivity-hub" + registration_enabled: false + - zone_name: "privatelink.database.windows.net" + vnet_name: "vnet-azlc-iic-eus-connectivity-hub" + registration_enabled: false + - zone_name: "privatelink.azurecr.io" + vnet_name: "vnet-azlc-iic-eus-connectivity-hub" + registration_enabled: false + # Spoke VNet link + - zone_name: "privatelink.blob.core.windows.net" + vnet_name: "vnet-spoke1" + registration_enabled: false + - zone_name: "privatelink.blob.core.windows.net" + vnet_name: "vnet-azlc-iic-eus-connectivity-hub" + registration_enabled: false + + enable_private_dns_resolver: + type: boolean + description: "Enable Azure DNS Private Resolver in hub" + required: false + default: false + + private_dns_resolver_name: + type: string + description: "DNS Private Resolver name" + required: false + pattern: "^dnspr-[a-z0-9-]+$" + example: "dnspr-azlc-iic-eus" + + private_dns_resolver_inbound_endpoint: + type: string + description: "Inbound endpoint IP for DNS Private Resolver" + required: false + format: ipv4 + example: "192.168.7.4" + + private_dns_resolver_outbound_endpoint: + type: string + description: "Outbound endpoint for DNS Private Resolver forwarding rules" + required: false + pattern: "^dnspr-[a-z0-9-]+-out$" + example: "dnspr-azlc-iic-eus-out" + + nat_gateways: + type: array + description: "NAT gateways for outbound connectivity" + required: false + items: + type: object + properties: + name: + type: string + public_ip_prefixes: + type: array + example: + - name: "nat-gw-spoke1" + public_ip_prefixes: + - "pip-prefix1" + + public_ip_prefixes: + type: array + description: "Public IP prefixes for scalable IPs" + required: false + items: + type: object + properties: + name: + type: string + prefix_length: + type: integer + example: + - name: "pip-prefix-bastion" + prefix_length: 28 + + ddos_protection_plans: + type: array + description: "DDoS protection plans per VNet" + required: false + items: + type: object + properties: + name: + type: string + enabled: + type: boolean + example: + - name: "ddos-plan-hub" + enabled: true + + network_watchers: + type: array + description: "Network watchers per region" + required: false + items: + type: object + properties: + name: + type: string + location: + type: string + example: + - name: "nw-eastus" + location: "East US" + + enable_network_watcher_flow_logs: + type: boolean + description: "Enable NSG flow logs through Network Watcher" + required: false + default: false + + enable_connection_monitor: + type: boolean + description: "Enable Network Watcher connection monitoring" + required: false + default: false + + connection_monitor_workspace: + type: string + description: "Log Analytics workspace for connection monitoring" + required: false + format: uuid + example: "12345678-1234-1234-1234-123456789012" + + enable_network_topology_view: + type: boolean + description: "Enable Network Watcher topology visualization" + required: false + default: true + + hub_spoke_topology_name: + type: string + description: "Name/identifier for hub-spoke topology" + required: false + example: "azlc-iic-hub-spoke-topology" + +# ============================================================================= +# CLOUD NETWORKING - AZURE VNETS +# ============================================================================= + +azure_vnets: + vnet_name: + type: string + description: "Virtual network name" + required: true + pattern: "^vnet-[a-z0-9-]+$" + example: "vnet-azlc-iic-eus-landingzone-prod" + + vnet_address_space: + type: array + description: "VNet address space (CIDR)" + required: true + items: + type: string + format: cidr + example: + - "10.0.0.0/16" + + vnet_resource_group: + type: string + description: "VNet resource group" + required: true + pattern: "^rg-[a-z0-9-]+$" + example: "rg-azlc-iic-eus-landingzone-prod" + + vnet_subscription: + type: string + description: "VNet subscription ID" + required: true + format: uuid + example: "00000000-1111-2222-3333-444444444444" + + vnet_location: + type: string + description: "VNet location" + required: true + example: "East US" + + vnet_enable_ddos_protection: + type: boolean + description: "Enable DDoS protection plan (requires DDoS protection plan resource)" + required: false + default: false + + vnet_ddos_protection_plan_id: + type: string + description: "Resource ID of DDoS protection plan (required if enable_ddos_protection is true)" + required: false + example: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-connectivity-hub/providers/Microsoft.Network/ddosProtectionPlans/ddos-plan-azlc" + + vnet_enable_vm_protection: + type: boolean + description: "Enable VM protection (DDoS standard)" + required: false + default: false + + vnet_bgp_community: + type: string + description: "BGP community value for advertising routes from this VNet" + required: false + pattern: "^[0-9]+:[0-9]+$" + example: "12076:20001" + + vnet_cidr: + type: string + description: "VNet CIDR block" + required: true + format: cidr + example: "10.0.0.0/16" + + subnet_name: + category: "networking" + subcategory: "subnet" + type: string + description: "Subnet name" + required: true + pattern: "^snet-[a-z0-9-]+$" + example: "snet-azlc-iic-eus-landingzone-prod-web" + + subnet_cidr: + type: string + description: "Subnet CIDR" + required: true + format: cidr + example: "10.0.1.0/24" + + subnet_address_prefix: + category: "networking" + subcategory: "subnet" + type: string + description: "Subnet address prefix" + required: true + format: cidr + example: "10.0.1.0/24" + + subnets: + type: array + description: "Array of subnets in the VNet" + required: false + items: + type: object + properties: + name: + type: string + address_prefix: + type: string + format: cidr + nsg: + type: string + route_table: + type: string + service_endpoints: + type: array + delegations: + type: array + private_endpoint_network_policies: + type: string + private_link_service_network_policies: + type: string + nat_gateway: + type: string + example: + - name: "snet-azlc-iic-eus-landingzone-web" + address_prefix: "10.0.1.0/24" + nsg: "nsg-azlc-iic-eus-web" + route_table: "rt-azlc-iic-eus-web" + service_endpoints: + - "Microsoft.Storage" + - "Microsoft.KeyVault" + private_endpoint_network_policies: "Disabled" + nat_gateway: "nat-azlc-iic-eus-web" + + subnet_delegation_name: + type: string + description: "Subnet delegation name for Azure services" + required: false + example: "Microsoft.Web/serverFarms" + + subnet_delegation_service_name: + type: string + description: "Service name for subnet delegation" + required: false + allowedValues: + - Microsoft.Web/serverFarms + - Microsoft.ContainerInstance/containerGroups + - Microsoft.Netapp/volumes + - Microsoft.HardwareSecurityModules/dedicatedHSMs + - Microsoft.ServiceFabricMesh/networks + - Microsoft.Logic/integrationServiceEnvironments + - Microsoft.Batch/batchAccounts + - Microsoft.Sql/managedInstances + - Microsoft.DBforPostgreSQL/serversv2 + - Microsoft.DBforPostgreSQL/flexibleServers + - Microsoft.DBforMySQL/flexibleServers + - Microsoft.ApiManagement/service + - Microsoft.Synapse/workspaces + example: "Microsoft.Web/serverFarms" + + subnet_private_endpoint_network_policies: + type: string + description: "Enable or disable private endpoint network policies on subnet" + required: false + allowedValues: + - Enabled + - Disabled + default: "Disabled" + + subnet_private_link_service_network_policies: + type: string + description: "Enable or disable private link service network policies on subnet" + required: false + allowedValues: + - Enabled + - Disabled + default: "Enabled" + + vnet_peering_name: + type: string + description: "VNet peering name" + required: false + example: "peer-azlc-to-hub" + + vnet_peering_remote_vnet: + type: string + description: "Remote VNet for peering" + required: false + pattern: "^vnet-[a-z0-9-]+$" + example: "vnet-azlc-iic-eus-connectivity-hub" + + allow_forwarded_traffic: + type: boolean + description: "Allow forwarded traffic" + required: false + default: true + + allow_gateway_transit: + type: boolean + description: "Allow gateway transit" + required: false + default: false + + allow_virtual_network_access: + type: boolean + description: "Allow virtual network access" + required: false + default: true + + use_remote_gateways: + type: boolean + description: "Use remote VNet's gateway (requires allow_gateway_transit on remote VNet)" + required: false + default: false + + peer_complete_virtual_network: + type: boolean + description: "Peer the complete virtual network address space" + required: false + default: true + + vnet_peering_remote_vnet_id: + type: string + description: "Full resource ID of remote VNet for peering" + required: false + example: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-connectivity-hub/providers/Microsoft.Network/virtualNetworks/vnet-azlc-iic-eus-connectivity-hub" + + vnet_additional_subnets: + type: array + description: "Additional subnets in the VNet" + required: false + items: + type: object + properties: + name: + type: string + address_prefix: + type: string + format: cidr + nsg: + type: string + example: + - name: "snet-app" + address_prefix: "10.0.2.0/24" + nsg: "nsg-app" + + vnet_service_endpoints: + type: array + description: "Service endpoints for the VNet" + required: false + items: + type: string + example: + - "Microsoft.Storage" + - "Microsoft.Sql" + + vnet_service_endpoint_policies: + type: array + description: "Service endpoint policies for VNet subnets" + required: false + items: + type: object + properties: + name: + type: string + service: + type: string + service_resources: + type: array + example: + - name: "sep-storage-policy" + service: "Microsoft.Storage" + service_resources: + - "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-workload-storage/providers/Microsoft.Storage/storageAccounts/stazlciiceus001" + + vnet_encryption_enabled: + type: boolean + description: "Enable VNet encryption (requires supported VM SKUs)" + required: false + default: false + + vnet_encryption_enforcement: + type: string + description: "VNet encryption enforcement mode" + required: false + allowedValues: + - AllowUnencrypted + - DropUnencrypted + default: "AllowUnencrypted" + + vnet_flow_timeout_minutes: + type: integer + description: "Flow timeout in minutes for VNet traffic" + required: false + minimum: 4 + maximum: 30 + default: 4 + + vnet_enable_flow_logs: + type: boolean + description: "Enable NSG flow logs for VNet subnets" + required: false + default: false + + vnet_flow_logs_retention_days: + type: integer + description: "Retention days for flow logs" + required: false + minimum: 0 + maximum: 365 + default: 30 + + vnet_flow_logs_traffic_analytics_enabled: + type: boolean + description: "Enable traffic analytics for flow logs" + required: false + default: false + + vnet_flow_logs_workspace_id: + type: string + description: "Log Analytics workspace ID for traffic analytics" + required: false + format: uuid + example: "12345678-1234-1234-1234-123456789012" + + vnet_dns_servers: + type: array + description: "Custom DNS servers for the VNet" + required: false + items: + type: string + format: ipv4 + example: + - "10.100.1.36" + - "10.100.1.37" + + vnet_enable_private_dns_resolver: + type: boolean + description: "Enable Azure DNS Private Resolver for VNet" + required: false + default: false + + vnet_private_dns_resolver_inbound_subnet: + type: string + description: "Subnet for DNS Private Resolver inbound endpoints" + required: false + pattern: "^snet-[a-z0-9-]+$" + example: "snet-azlc-iic-eus-connectivity-dns-inbound" + + vnet_private_dns_resolver_outbound_subnet: + type: string + description: "Subnet for DNS Private Resolver outbound endpoints" + required: false + pattern: "^snet-[a-z0-9-]+$" + example: "snet-azlc-iic-eus-connectivity-dns-outbound" + + vnet_tags: + type: object + description: "Tags for the VNet" + required: false + example: + environment: "prod" + project: "azlc" + cost_center: "IT-Infrastructure" + workload: "landingzone" + +# ============================================================================= +# CLOUD NETWORKING - VPN +# ============================================================================= + +vpn: + vpn_gateways: + type: array + description: "VPN gateways per tenant" + required: false + items: + type: object + properties: + name: + type: string + sku: + type: string + public_ip: + type: string + bgp_enabled: + type: boolean + bgp_asn: + type: integer + example: + - name: "vpngw-tenant1" + sku: "VpnGw1" + public_ip: "pip-vpngw-tenant1" + bgp_enabled: true + bgp_asn: 65515 + + vpn_gateway_name: + type: string + description: "VPN gateway name" + required: false + example: "vpngw-azlc-iic-eus" + + vpn_gateway_type: + type: string + description: "VPN gateway type" + required: false + allowedValues: + - Vpn + - ExpressRoute + default: "Vpn" + + vpn_gateway_vpn_type: + type: string + description: "VPN type for route-based or policy-based VPN" + required: false + allowedValues: + - RouteBased + - PolicyBased + default: "RouteBased" + + vpn_gateway_sku: + type: string + description: "VPN gateway SKU" + required: false + allowedValues: + - Basic + - VpnGw1 + - VpnGw2 + - VpnGw3 + - VpnGw4 + - VpnGw5 + - VpnGw1AZ + - VpnGw2AZ + - VpnGw3AZ + - VpnGw4AZ + - VpnGw5AZ + default: "VpnGw1" + + vpn_gateway_generation: + type: string + description: "VPN gateway generation" + required: false + allowedValues: + - Generation1 + - Generation2 + default: "Generation2" + + vpn_gateway_active_active: + type: boolean + description: "Enable active-active mode for high availability" + required: false + default: false + + vpn_gateway_subnet_address_prefix: + type: string + description: "Gateway subnet address prefix (must be named GatewaySubnet)" + required: false + format: cidr + example: "192.168.0.0/27" + + vpn_gateway_public_ip: + type: string + description: "VPN gateway public IP name" + required: false + pattern: "^pip-[a-z0-9-]+$" + example: "pip-azlc-iic-eus-vpngw" + + vpn_gateway_public_ip_address: + type: string + description: "VPN gateway public IP address" + required: false + format: ipv4 + example: "assigned by Azure" + + vpn_gateway_bgp_enabled: + type: boolean + description: "BGP enabled on VPN gateway" + required: false + default: false + + vpn_gateway_bgp_asn: + type: integer + description: "BGP ASN for VPN gateway" + required: false + minimum: 64512 + maximum: 65534 + example: 65515 + + vpn_gateway_bgp_peering_address: + type: string + description: "BGP peering address" + required: false + format: ipv4 + example: "192.168.0.4" + + local_gateways: + type: array + description: "Local network gateways" + required: false + items: + type: object + properties: + name: + type: string + ip_address: + type: string + format: ipv4 + address_prefixes: + type: array + bgp_asn: + type: integer + example: + - name: "lng-onprem" + ip_address: "203.0.113.1" + address_prefixes: + - "192.168.0.0/16" + bgp_asn: 65001 + + local_gateway_name: + type: string + description: "Local network gateway name" + required: false + example: "lng-azlc-iic-onprem" + + local_gateway_device: + type: string + description: "Local gateway device model" + required: false + example: "Cisco ASA" + + local_gateway_ip: + type: string + description: "Local gateway IP address" + required: false + format: ipv4 + example: "203.0.113.1" + + local_gateway_address_prefixes: + type: array + description: "Local gateway address prefixes" + required: false + items: + type: string + format: cidr + example: + - "192.168.0.0/16" + + local_gateway_bgp_asn: + type: integer + description: "Local gateway BGP ASN" + required: false + example: 65001 + + local_gateway_bgp_peering_address: + type: string + description: "Local gateway BGP peering address" + required: false + format: ipv4 + example: "192.168.0.1" + + vpn_connections: + type: array + description: "VPN connections" + required: false + items: + type: object + properties: + name: + type: string + type: + type: string + local_gateway: + type: string + shared_key: + type: string + sensitive: true + enable_bgp: + type: boolean + example: + - name: "conn-to-onprem" + type: "IPsec" + local_gateway: "lng-onprem" + shared_key: "secret" + enable_bgp: true + + vpn_connection_name: + type: string + description: "VPN connection name" + required: false + example: "conn-azlc-to-onprem" + + vpn_connection_type: + type: string + description: "VPN connection type" + required: false + allowedValues: + - IPsec + - Vnet2Vnet + - ExpressRoute + default: "IPsec" + + vpn_connection_protocol: + type: string + description: "VPN connection protocol" + required: false + allowedValues: + - IKEv2 + - IKEv1 + default: "IKEv2" + + vpn_connection_enable_bgp: + type: boolean + description: "Enable BGP on connection" + required: false + default: false + + vpn_connection_routing_weight: + type: integer + description: "Connection routing weight" + required: false + minimum: 0 + maximum: 32000 + default: 100 + + vpn_connection_mode: + type: string + description: "VPN connection mode" + required: false + allowedValues: + - Default + - InitiatorOnly + - ResponderOnly + default: "Default" + + vpn_connection_dpd_timeout_seconds: + type: integer + description: "Dead Peer Detection timeout in seconds" + required: false + minimum: 9 + maximum: 3600 + default: 45 + + vpn_connection_use_policy_based_traffic_selectors: + type: boolean + description: "Enable policy-based traffic selectors for compatibility with on-prem devices" + required: false + default: false + + vpn_connection_ipsec_policies: + type: array + description: "Custom IPsec/IKE policies for VPN connection" + required: false + items: + type: object + properties: + ike_encryption: + type: string + ike_integrity: + type: string + dh_group: + type: string + ipsec_encryption: + type: string + ipsec_integrity: + type: string + pfs_group: + type: string + sa_lifetime_seconds: + type: integer + sa_data_size_kilobytes: + type: integer + example: + - ike_encryption: "AES256" + ike_integrity: "SHA256" + dh_group: "DHGroup14" + ipsec_encryption: "AES256" + ipsec_integrity: "SHA256" + pfs_group: "PFS2048" + sa_lifetime_seconds: 27000 + sa_data_size_kilobytes: 102400000 + + vpn_shared_key_secret: + type: string + description: "VPN shared key (Key Vault reference)" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-mgmt/vpn-shared-key" + + p2s_client_address_pool: + type: string + description: "Point-to-site client address pool (CIDR)" + required: false + format: cidr + example: "172.16.0.0/24" + + p2s_vpn_client_protocols: + type: array + description: "Point-to-site VPN client protocols" + required: false + items: + type: string + allowedValues: + - IkeV2 + - SSTP + - OpenVPN + example: + - "IkeV2" + - "OpenVPN" + + p2s_authentication_type: + type: string + description: "Point-to-site authentication type" + required: false + allowedValues: + - Certificate + - AAD + - Radius + default: "Certificate" + + vpn_client_root_certificate_name: + type: string + description: "VPN client root certificate name" + required: false + example: "P2SRootCert" + + bgp_enabled: + type: boolean + description: "Global BGP flag" + required: false + default: false + + bgp_asn: + type: integer + description: "Tenant BGP ASN" + required: false + minimum: 64512 + maximum: 65534 + example: 65515 + + bgp_peering_addresses: + type: array + description: "BGP peering addresses" + required: false + items: + type: string + format: ipv4 + example: + - "192.168.0.4" + - "192.168.0.5" + + on_prem_networks: + type: array + description: "On-prem networks to advertise via BGP" + required: false + items: + type: string + format: cidr + example: + - "192.168.0.0/16" + - "10.0.0.0/8" + + route_filters: + type: array + description: "BGP route filters" + required: false + items: + type: object + properties: + name: + type: string + rules: + type: array + example: + - name: "rf-onprem" + rules: + - name: "allow-all" + access: "Allow" + bgp_community: "*" + + vpn_connection_bgp_settings: + type: object + description: "BGP settings per connection" + required: false + properties: + asn: + type: integer + peering_addresses: + type: array + example: + asn: 65515 + peering_addresses: + - "192.168.0.4" + +# ============================================================================= +# CLOUD NETWORKING - AZURE BASTION +# ============================================================================= + +bastion: + bastion_hosts: + type: array + description: "Bastion hosts per tenant" + required: false + items: + type: object + properties: + name: + type: string + sku: + type: string + public_ip: + type: string + vnet: + type: string + example: + - name: "bastion-tenant1" + sku: "Standard" + public_ip: "pip-bastion-tenant1" + vnet: "vnet-tenant1" + + bastion_name: + type: string + description: "Bastion name" + required: false + example: "bastion-azlc-iic-eus" + + bastion_sku: + type: string + description: "Bastion SKU" + required: false + allowedValues: + - Basic + - Standard + default: "Standard" + + bastion_public_ip_name: + type: string + description: "Bastion public IP name" + required: false + pattern: "^pip-[a-z0-9-]+$" + example: "pip-bastion-azlc-iic-eus" + + bastion_resource_group: + type: string + description: "Bastion resource group" + required: false + pattern: "^rg-[a-z0-9-]+$" + example: "rg-azlc-iic-eus-connectivity-hub" + + bastion_subscription: + type: string + description: "Bastion subscription" + required: false + format: uuid + example: "00000000-1111-2222-3333-444444444444" + + bastion_location: + type: string + description: "Bastion deployment location" + required: false + example: "East US" + + bastion_vnet: + type: string + description: "Bastion virtual network" + required: false + pattern: "^vnet-[a-z0-9-]+$" + example: "vnet-azlc-iic-eus-connectivity-hub" + + bastion_subnet: + type: string + description: "Bastion subnet" + required: false + default: "AzureBastionSubnet" + + bastion_scale_units: + type: integer + description: "Bastion scale units (2-50, Standard SKU only)" + required: false + minimum: 2 + maximum: 50 + default: 2 + + bastion_enable_copy_paste: + type: boolean + description: "Enable copy/paste functionality (Standard SKU only)" + required: false + default: true + + bastion_enable_file_copy: + type: boolean + description: "Enable file copy upload/download (Standard SKU only)" + required: false + default: true + + bastion_enable_tunneling: + type: boolean + description: "Enable native client tunneling support (Standard SKU only)" + required: false + default: true + + bastion_enable_ip_connect: + type: boolean + description: "Enable IP connect (connect by IP address, Standard SKU only)" + required: false + default: true + + bastion_enable_shareable_link: + type: boolean + description: "Enable shareable link (Standard SKU only)" + required: false + default: true + + bastion_enable_kerberos: + type: boolean + description: "Enable Kerberos authentication for Windows VMs" + required: false + default: false + + bastion_enable_session_recording: + type: boolean + description: "Enable session recording for audit/compliance" + required: false + default: false + + bastion_tags: + type: object + description: "Tags for Bastion" + required: false + example: + environment: "iic" + function: "connectivity" + cost_center: "infrastructure" + + bastion_enable_diagnostics: + type: boolean + description: "Enable diagnostic settings for Bastion" + required: false + default: true + + bastion_diagnostics_workspace_id: + type: string + description: "Log Analytics workspace ID for Bastion diagnostics" + required: false + format: uuid + example: "12345678-1234-1234-1234-123456789012" + + bastion_log_categories: + type: array + description: "Log categories to enable for Bastion diagnostics" + required: false + items: + type: string + example: + - "BastionAuditLogs" + - "AllMetrics" + + bastion_availability_zones: + type: array + description: "Availability zones for Bastion deployment (Standard SKU only)" + required: false + items: + type: string + example: + - "1" + - "2" + - "3" + + bastion_disable_public_ip: + type: boolean + description: "Deploy Bastion without public IP (private-only mode, Premium SKU)" + required: false + default: false + + bastion_subnet_address_prefix: + type: string + description: "Address prefix for AzureBastionSubnet (minimum /26)" + required: false + format: cidr + example: "192.168.1.0/26" + + bastion_ip_configuration_name: + type: string + description: "Name for Bastion IP configuration" + required: false + default: "bastion-ip-config" + + bastion_dns_name: + type: string + description: "Custom DNS name label for Bastion public IP" + required: false + pattern: "^[a-z0-9-]+$" + example: "bastion-azlc-iic" + +# ============================================================================= +# CLOUD NETWORKING - AZURE FIREWALL +# ============================================================================= + +azure_firewall: + azure_fw_firewalls: + type: array + description: "Azure firewalls per tenant" + required: false + items: + type: object + properties: + name: + type: string + sku: + type: string + subnet: + type: string + azure_fw_policy: + type: string + example: + - name: "fw-tenant1" + sku: "AZFW_VNet" + subnet: "AzureFirewallSubnet" + azure_fw_policy: "fp-tenant1" + + azure_fw_name: + type: string + description: "Firewall name" + required: false + example: "fw-azlc-iic-eus" + + azure_fw_subscription: + type: string + description: "Firewall subscription ID" + required: false + format: uuid + example: "00000000-1111-2222-3333-444444444444" + + azure_fw_location: + type: string + description: "Firewall deployment location" + required: false + example: "East US" + + azure_fw_resource_group: + type: string + description: "Firewall resource group" + required: false + pattern: "^rg-[a-z0-9-]+$" + example: "rg-azlc-iic-eus-connectivity-hub" + + azure_fw_sku: + type: string + description: "Firewall SKU (deployment model)" + required: false + allowedValues: + - AZFW_VNet + - AZFW_Hub + default: "AZFW_VNet" + + azure_fw_tier: + type: string + description: "Firewall tier (Standard or Premium for advanced features)" + required: false + allowedValues: + - Standard + - Premium + - Basic + default: "Standard" + + azure_fw_subnet: + type: string + description: "Firewall subnet (must be named AzureFirewallSubnet)" + required: false + default: "AzureFirewallSubnet" + + azure_fw_subnet_address_prefix: + type: string + description: "Address prefix for AzureFirewallSubnet (minimum /26)" + required: false + format: cidr + example: "192.168.2.0/26" + + azure_fw_availability_zones: + type: array + description: "Availability zones for zone-redundant firewall deployment" + required: false + items: + type: string + example: + - "1" + - "2" + - "3" + + azure_fw_vnet: + type: string + description: "Firewall virtual network" + required: false + pattern: "^vnet-[a-z0-9-]+$" + example: "vnet-azlc-iic-eus-connectivity-hub" + + azure_fw_public_ip: + type: string + description: "Firewall public IP" + required: false + pattern: "^pip-[a-z0-9-]+$" + example: "pip-fw-azlc-iic-eus" + + azure_fw_public_ip_count: + type: integer + description: "Number of public IPs for SNAT port scaling" + required: false + minimum: 1 + maximum: 250 + default: 1 + + azure_fw_enable_forced_tunneling: + type: boolean + description: "Enable forced tunneling (requires management subnet)" + required: false + default: false + + azure_fw_management_subnet: + type: string + description: "Management subnet for forced tunneling (must be AzureFirewallManagementSubnet)" + required: false + default: "AzureFirewallManagementSubnet" + + azure_fw_management_subnet_address_prefix: + type: string + description: "Address prefix for management subnet (minimum /26)" + required: false + format: cidr + example: "192.168.2.64/26" + + azure_fw_management_public_ip: + type: string + description: "Management public IP for forced tunneling" + required: false + pattern: "^pip-[a-z0-9-]+$" + example: "pip-fw-azlc-iic-eus-mgmt" + + azure_fw_policies: + type: array + description: "Firewall policies (supports hierarchical policy inheritance)" + required: false + items: + type: object + properties: + name: + type: string + tier: + type: string + base_policy: + type: string + threat_intel_mode: + type: string + rule_collection_groups: + type: array + example: + - name: "fp-azlc-iic" + tier: "Standard" + threat_intel_mode: "Alert" + rule_collection_groups: + - name: "rcg-network-rules" + priority: 100 + - name: "rcg-application-rules" + priority: 200 + + azure_fw_policy_name: + type: string + description: "Primary firewall policy name" + required: false + pattern: "^fp-[a-z0-9-]+$" + example: "fp-azlc-iic-eus" + + azure_fw_policy_tier: + type: string + description: "Firewall policy tier (must match firewall tier)" + required: false + allowedValues: + - Standard + - Premium + - Basic + default: "Standard" + + azure_fw_policy_base_policy_id: + type: string + description: "Base policy ID for hierarchical policy inheritance" + required: false + example: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-connectivity-hub/providers/Microsoft.Network/firewallPolicies/fp-azlc-base" + + azure_fw_threat_intel_mode: + type: string + description: "Threat intelligence mode" + required: false + allowedValues: + - Alert + - Deny + - Off + default: "Alert" + + azure_fw_threat_intel_allowlist: + type: object + description: "Threat intelligence allowlist (IP addresses and FQDNs)" + required: false + properties: + ip_addresses: + type: array + fqdns: + type: array + example: + ip_addresses: + - "203.0.113.0/24" + fqdns: + - "trusted.example.com" + + azure_fw_enable_idps: + type: boolean + description: "Enable Intrusion Detection and Prevention System (Premium tier only)" + required: false + default: false + + azure_fw_idps_mode: + type: string + description: "IDPS mode (Premium tier only)" + required: false + allowedValues: + - Alert + - Deny + - Off + default: "Alert" + + azure_fw_idps_signature_overrides: + type: array + description: "IDPS signature overrides (Premium tier)" + required: false + items: + type: object + example: + - signature_id: "2024001" + mode: "Deny" + + azure_fw_enable_tls_inspection: + type: boolean + description: "Enable TLS inspection for HTTPS traffic (Premium tier only)" + required: false + default: false + + azure_fw_tls_certificate_key_vault_id: + type: string + description: "Key Vault ID for TLS inspection certificate (Premium tier)" + required: false + example: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-connectivity-hub/providers/Microsoft.KeyVault/vaults/kv-azlc-mgmt" + + azure_fw_dns_proxy_enabled: + type: boolean + description: "DNS proxy enabled" + required: false + default: true + + azure_fw_dns_servers: + type: array + description: "Custom DNS servers for firewall (leave empty for Azure DNS)" + required: false + items: + type: string + format: ipv4 + example: + - "10.100.1.36" + - "10.100.1.37" + + azure_fw_private_ip_ranges: + type: array + description: "Private IP ranges for SNAT bypass (RFC 1918 by default)" + required: false + items: + type: string + format: cidr + example: + - "10.0.0.0/8" + - "172.16.0.0/12" + - "192.168.0.0/16" + + azure_fw_enable_diagnostics: + type: boolean + description: "Enable diagnostic settings for Azure Firewall" + required: false + default: true + + azure_fw_diagnostics_workspace_id: + type: string + description: "Log Analytics workspace ID for firewall diagnostics" + required: false + format: uuid + example: "12345678-1234-1234-1234-123456789012" + + azure_fw_log_categories: + type: array + description: "Log categories to enable for firewall diagnostics" + required: false + items: + type: string + example: + - "AzureFirewallApplicationRule" + - "AzureFirewallNetworkRule" + - "AzureFirewallDnsProxy" + - "AZFWApplicationRule" + - "AZFWNetworkRule" + - "AZFWNatRule" + - "AZFWThreatIntel" + - "AZFWIdpsSignature" + - "AZFWDnsQuery" + - "AllMetrics" + + azure_fw_virtual_hub_id: + type: string + description: "Virtual Hub ID for AZFW_Hub SKU deployment" + required: false + example: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-connectivity-hub/providers/Microsoft.Network/virtualHubs/vhub-azlc-iic-eus" + + azure_fw_hub_public_ip_count: + type: integer + description: "Number of public IPs for Virtual WAN secured hub" + required: false + minimum: 1 + maximum: 100 + default: 1 + + azure_fw_tags: + type: object + description: "Firewall tags" + required: false + example: + environment: "iic" + function: "connectivity" + cost_center: "infrastructure" + tier: "Standard" + + +# ============================================================================= +# AZURE VIRTUAL MACHINES +# ============================================================================= + +azure_vms: + # DOMAIN CONTROLLERS + # --------------------------------------------------------------------------- + dc1: + azure_vm_dc1_vm_name: + category: "compute" + subcategory: "domain-controller" + type: string + description: "DC1 Azure VM resource name" + required: true + example: "vm-azlc-iic-eus-dc01" + + azure_vm_dc1_computer_name: + category: "compute" + subcategory: "domain-controller" + type: string + description: "DC1 Windows Server computer name" + required: true + maxLength: 15 + example: "DC01" + + azure_vm_dc1_vm_size: + type: string + description: "DC1 VM size" + required: true + example: "Standard_D2s_v3" + + azure_vm_dc1_image_publisher: + type: string + description: "DC1 image publisher" + required: true + default: "MicrosoftWindowsServer" + + azure_vm_dc1_image_offer: + type: string + description: "DC1 image offer" + required: true + default: "WindowsServer" + + azure_vm_dc1_image_sku: + type: string + description: "DC1 image SKU" + required: true + default: "2025-datacenter-azure-edition" + + azure_vm_dc1_image_version: + type: string + description: "DC1 image version" + required: true + default: "latest" + + azure_vm_dc1_private_ip: + type: string + description: "DC1 private IP address" + required: true + format: ipv4 + example: "10.100.1.36" + + azure_vm_dc1_subnet: + type: string + description: "DC1 subnet name" + required: true + example: "snet-azlc-iic-eus-identity" + + azure_vm_dc1_os_disk_size_gb: + type: integer + description: "DC1 OS disk size (GB)" + required: true + default: 128 + + azure_vm_dc1_os_disk_type: + type: string + description: "DC1 OS disk type" + required: true + default: "Premium_LRS" + + azure_vm_dc1_data_disks: + type: array + description: "DC1 data disks" + required: false + items: + name: string + size_gb: integer + type: string + example: + - name: "disk-dc01-data-01" + size_gb: 256 + type: "Premium_LRS" + + dc2: + azure_vm_dc2_vm_name: + type: string + description: "DC2 Azure VM resource name" + required: true + example: "vm-azlc-iic-eus-dc02" + + azure_vm_dc2_computer_name: + type: string + description: "DC2 Windows Server computer name" + required: true + maxLength: 15 + example: "DC02" + + azure_vm_dc2_vm_size: + type: string + description: "DC2 VM size" + required: true + example: "Standard_D2s_v3" + + azure_vm_dc2_image_publisher: + type: string + description: "DC2 image publisher" + required: true + default: "MicrosoftWindowsServer" + + azure_vm_dc2_image_offer: + type: string + description: "DC2 image offer" + required: true + default: "WindowsServer" + + azure_vm_dc2_image_sku: + type: string + description: "DC2 image SKU" + required: true + default: "2025-datacenter-azure-edition" + + azure_vm_dc2_image_version: + type: string + description: "DC2 image version" + required: true + default: "latest" + + azure_vm_dc2_private_ip: + type: string + description: "DC2 private IP address" + required: true + format: ipv4 + example: "10.100.1.37" + + azure_vm_dc2_subnet: + type: string + description: "DC2 subnet name" + required: true + example: "snet-azlc-iic-eus-identity" + + azure_vm_dc2_os_disk_size_gb: + type: integer + description: "DC2 OS disk size (GB)" + required: true + default: 128 + + azure_vm_dc2_os_disk_type: + type: string + description: "DC2 OS disk type" + required: true + default: "Premium_LRS" + + azure_vm_dc2_data_disks: + type: array + description: "DC2 data disks" + required: false + items: + name: string + size_gb: integer + type: string + example: + - name: "disk-dc02-data-01" + size_gb: 256 + type: "Premium_LRS" + + # JUMPBOX + # --------------------------------------------------------------------------- + jumpbox: + azure_vm_jumpbox_vm_name: + type: string + description: "Jumpbox Azure VM resource name" + required: true + example: "vm-azlc-iic-eus-jumpbox" + + azure_vm_jumpbox_computer_name: + type: string + description: "Jumpbox Windows Server computer name" + required: true + maxLength: 15 + example: "JUMPBOX" + + azure_vm_jumpbox_vm_size: + type: string + description: "Jumpbox VM size" + required: true + example: "Standard_D2s_v3" + + azure_vm_jumpbox_image_publisher: + type: string + description: "Jumpbox image publisher" + required: true + default: "MicrosoftWindowsServer" + + azure_vm_jumpbox_image_offer: + type: string + description: "Jumpbox image offer" + required: true + default: "WindowsServer" + + azure_vm_jumpbox_image_sku: + type: string + description: "Jumpbox image SKU" + required: true + default: "2025-datacenter-azure-edition" + + azure_vm_jumpbox_image_version: + type: string + description: "Jumpbox image version" + required: true + default: "latest" + + azure_vm_jumpbox_private_ip: + type: string + description: "Jumpbox private IP address" + required: true + format: ipv4 + example: "10.100.1.50" + + azure_vm_jumpbox_subnet: + type: string + description: "Jumpbox subnet name" + required: true + example: "snet-azlc-iic-eus-management" + + azure_vm_jumpbox_os_disk_size_gb: + type: integer + description: "Jumpbox OS disk size (GB)" + required: true + default: 128 + + azure_vm_jumpbox_os_disk_type: + type: string + description: "Jumpbox OS disk type" + required: true + default: "Premium_LRS" + + # WAC SERVER (Windows Admin Center) + # --------------------------------------------------------------------------- + wac: + azure_vm_wac_vm_name: + type: string + description: "WAC Azure VM resource name" + required: true + example: "vm-azlc-iic-eus-wac" + + azure_vm_wac_computer_name: + type: string + description: "WAC Windows Server computer name" + required: true + maxLength: 15 + example: "WAC" + + azure_vm_wac_vm_size: + type: string + description: "WAC VM size" + required: true + example: "Standard_D2s_v3" + + azure_vm_wac_image_publisher: + type: string + description: "WAC image publisher" + required: true + default: "MicrosoftWindowsServer" + + azure_vm_wac_image_offer: + type: string + description: "WAC image offer" + required: true + default: "WindowsServer" + + azure_vm_wac_image_sku: + type: string + description: "WAC image SKU" + required: true + default: "2025-datacenter-azure-edition" + + azure_vm_wac_image_version: + type: string + description: "WAC image version" + required: true + default: "latest" + + azure_vm_wac_private_ip: + type: string + description: "WAC private IP address" + required: true + format: ipv4 + example: "10.100.1.51" + + azure_vm_wac_subnet: + type: string + description: "WAC subnet name" + required: true + example: "snet-azlc-iic-eus-management" + + azure_vm_wac_os_disk_size_gb: + type: integer + description: "WAC OS disk size (GB)" + required: true + default: 128 + + azure_vm_wac_os_disk_type: + type: string + description: "WAC OS disk type" + required: true + default: "Premium_LRS" + + # LIGHTHOUSE SERVER (Linux - Azure Lighthouse Management) + # --------------------------------------------------------------------------- + lighthouse: + azure_vm_lighthouse_vm_name: + type: string + description: "Lighthouse Azure VM resource name" + required: true + example: "vm-azlc-iic-eus-lighthouse" + + azure_vm_lighthouse_computer_name: + type: string + description: "Lighthouse Linux hostname" + required: true + maxLength: 64 + example: "lighthouse" + + azure_vm_lighthouse_vm_size: + type: string + description: "Lighthouse VM size" + required: true + example: "Standard_B2s" + + azure_vm_lighthouse_image_publisher: + type: string + description: "Lighthouse image publisher" + required: true + default: "Canonical" + + azure_vm_lighthouse_image_offer: + type: string + description: "Lighthouse image offer" + required: true + default: "0001-com-ubuntu-server-jammy" + + azure_vm_lighthouse_image_sku: + type: string + description: "Lighthouse image SKU" + required: true + default: "22_04-lts-gen2" + + azure_vm_lighthouse_image_version: + type: string + description: "Lighthouse image version" + required: true + default: "latest" + + azure_vm_lighthouse_private_ip: + type: string + description: "Lighthouse private IP address" + required: true + format: ipv4 + example: "10.100.1.60" + + azure_vm_lighthouse_subnet: + type: string + description: "Lighthouse subnet name" + required: true + example: "snet-azlc-iic-eus-management" + + azure_vm_lighthouse_os_disk_size_gb: + type: integer + description: "Lighthouse OS disk size (GB)" + required: true + default: 64 + + azure_vm_lighthouse_os_disk_type: + type: string + description: "Lighthouse OS disk type" + required: true + default: "StandardSSD_LRS" + + # SYSLOG/SNMP SERVER (Linux) + # --------------------------------------------------------------------------- + syslog: + azure_vm_syslog_vm_name: + type: string + description: "Syslog Azure VM resource name" + required: true + example: "vm-azlc-iic-eus-syslog" + + azure_vm_syslog_computer_name: + type: string + description: "Syslog Linux hostname" + required: true + maxLength: 64 + example: "syslog" + + azure_vm_syslog_vm_size: + type: string + description: "Syslog VM size" + required: true + example: "Standard_B2s" + + azure_vm_syslog_image_publisher: + type: string + description: "Syslog image publisher" + required: true + default: "Canonical" + + azure_vm_syslog_image_offer: + type: string + description: "Syslog image offer" + required: true + default: "0001-com-ubuntu-server-jammy" + + azure_vm_syslog_image_sku: + type: string + description: "Syslog image SKU" + required: true + default: "22_04-lts-gen2" + + azure_vm_syslog_image_version: + type: string + description: "Syslog image version" + required: true + default: "latest" + + azure_vm_syslog_private_ip: + type: string + description: "Syslog private IP address" + required: true + format: ipv4 + example: "10.100.1.61" + + azure_vm_syslog_subnet: + type: string + description: "Syslog subnet name" + required: true + example: "snet-azlc-iic-eus-management" + + azure_vm_syslog_os_disk_size_gb: + type: integer + description: "Syslog OS disk size (GB)" + required: true + default: 64 + + azure_vm_syslog_os_disk_type: + type: string + description: "Syslog OS disk type" + required: true + default: "StandardSSD_LRS" + + azure_vm_syslog_data_disks: + type: array + description: "Syslog data disks for log storage" + required: false + items: + name: string + size_gb: integer + type: string + example: + - name: "disk-syslog-logs-01" + size_gb: 512 + type: "StandardSSD_LRS" + + # SHARED VM SETTINGS + # --------------------------------------------------------------------------- + vm_shared: + azure_vm_resource_group: + type: string + description: "Azure VMs resource group" + required: true + example: "rg-azlc-iic-eus-management-compute" + + azure_vm_location: + type: string + description: "Azure VMs location" + required: true + default: "East US" + + azure_vm_availability_zones: + type: array + description: "Availability zones for VM deployment (regional HA)" + required: false + items: + type: string + example: ["1", "2", "3"] + + azure_vm_accelerated_networking: + type: boolean + description: "Enable accelerated networking (SR-IOV) for all VMs" + required: false + default: true + + azure_vm_license_type: + type: string + description: "Azure Hybrid Benefit license type" + required: false + options: + - "Windows_Server" + - "Windows_Client" + - "RHEL_BYOS" + - "SLES_BYOS" + - "None" + default: "None" + + azure_vm_security_type: + type: string + description: "VM security type (Gen2 VMs)" + required: false + options: + - "TrustedLaunch" + - "ConfidentialVM" + - "Standard" + default: "TrustedLaunch" + + azure_vm_secure_boot_enabled: + type: boolean + description: "Enable secure boot (Trusted Launch/Confidential VMs)" + required: false + default: true + + azure_vm_vtpm_enabled: + type: boolean + description: "Enable vTPM (Trusted Launch/Confidential VMs)" + required: false + default: true + + azure_vm_os_disk_caching: + type: string + description: "Default OS disk caching mode" + required: false + options: + - "ReadWrite" + - "ReadOnly" + - "None" + default: "ReadWrite" + + azure_vm_boot_diagnostics_enabled: + type: boolean + description: "Enable boot diagnostics for all VMs" + required: false + default: true + + azure_vm_managed_identity_enabled: + type: boolean + description: "Enable system-assigned managed identity for all VMs" + required: false + default: true + + azure_vm_tags: + type: object + description: "Default tags applied to all VMs" + required: false + example: + Environment: "Production" + ManagedBy: "Terraform" + CostCenter: "IT-Infrastructure" + Workload: "Infrastructure" + +# ============================================================================= +# ON-PREM SECTION +# ============================================================================= + +# ============================================================================= +# ON-PREM NETWORKING - VLANS +# ============================================================================= + +vlans: + infrastructure_type: ["azure_local", "wsfc_s2d", "wsfc_san"] + # MANAGEMENT-COMPUTE NETWORK (Azure Local Nodes, Management VMs) + # --------------------------------------------------------------------------- + vlan_management_compute_id: + type: integer + description: "Management-Compute VLAN ID (Azure Local nodes, management VMs)" + required: true + minimum: 1 + maximum: 4094 + example: 211 + + vlan_management_compute_name: + type: string + description: "Management-Compute VLAN name" + required: true + example: "management-compute" + + vlan_management_compute_cidr: + type: string + description: "Management-Compute VLAN CIDR" + required: true + format: cidr + example: "192.168.211.0/24" + + vlan_management_compute_gateway: + type: string + description: "Management-Compute VLAN gateway" + required: true + format: ipv4 + example: "192.168.211.1" + + vlan_management_compute_dns_primary: + type: string + description: "Management-Compute VLAN primary DNS" + required: false + format: ipv4 + example: "10.100.1.36" + + vlan_management_compute_dns_secondary: + type: string + description: "Management-Compute VLAN secondary DNS" + required: false + format: ipv4 + example: "10.100.1.37" + + # OUT-OF-BAND NETWORK (iDRACs, Switches, Firewalls, OpenGear) + # --------------------------------------------------------------------------- + vlan_oob_id: + type: integer + description: "Out-of-band VLAN ID (iDRACs, switches, firewalls, OpenGear)" + required: true + minimum: 1 + maximum: 4094 + example: 210 + + vlan_oob_name: + type: string + description: "Out-of-band VLAN name" + required: true + example: "oob" + + vlan_oob_cidr: + type: string + description: "Out-of-band VLAN CIDR" + required: true + format: cidr + example: "192.168.210.0/24" + + vlan_oob_gateway: + type: string + description: "Out-of-band VLAN gateway" + required: true + format: ipv4 + example: "192.168.210.1" + + # STORAGE VLANS (1-4, Non-Routable, East/West Traffic Only) + # --------------------------------------------------------------------------- + vlan_storage_networks: + type: array + description: "Storage VLANs for Azure Local cluster (non-routable, east/west traffic only)" + required: false + minItems: 1 + maxItems: 4 + items: + type: object + properties: + vlan_id: + type: integer + minimum: 1 + maximum: 4094 + name: + type: string + cidr: + type: string + format: cidr + routable: + type: boolean + default: false + example: + - vlan_id: 712 + name: "storage-1" + cidr: "10.71.212.0/24" + routable: false + - vlan_id: 713 + name: "storage-2" + cidr: "10.71.213.0/24" + routable: false + + vlan_storage_1_id: + type: integer + description: "Storage VLAN 1 ID (non-routable)" + required: false + minimum: 1 + maximum: 4094 + example: 712 + + vlan_storage_1_name: + type: string + description: "Storage VLAN 1 name" + required: false + example: "storage-1" + + vlan_storage_1_cidr: + type: string + description: "Storage VLAN 1 CIDR (non-routable, east/west only)" + required: false + format: cidr + example: "10.71.212.0/24" + + vlan_storage_2_id: + type: integer + description: "Storage VLAN 2 ID (non-routable)" + required: false + minimum: 1 + maximum: 4094 + example: 713 + + vlan_storage_2_name: + type: string + description: "Storage VLAN 2 name" + required: false + example: "storage-2" + + vlan_storage_2_cidr: + type: string + description: "Storage VLAN 2 CIDR (non-routable, east/west only)" + required: false + format: cidr + example: "10.71.213.0/24" + + vlan_storage_3_id: + type: integer + description: "Storage VLAN 3 ID (non-routable)" + required: false + minimum: 1 + maximum: 4094 + example: 714 + + vlan_storage_3_name: + type: string + description: "Storage VLAN 3 name" + required: false + example: "storage-3" + + vlan_storage_3_cidr: + type: string + description: "Storage VLAN 3 CIDR (non-routable, east/west only)" + required: false + format: cidr + example: "10.71.214.0/24" + + vlan_storage_4_id: + type: integer + description: "Storage VLAN 4 ID (non-routable)" + required: false + minimum: 1 + maximum: 4094 + example: 715 + + vlan_storage_4_name: + type: string + description: "Storage VLAN 4 name" + required: false + example: "storage-4" + + vlan_storage_4_cidr: + type: string + description: "Storage VLAN 4 CIDR (non-routable, east/west only)" + required: false + format: cidr + example: "10.71.215.0/24" + + # WORKLOAD NETWORKS (Customer Workloads, Up to 6 Networks) + # --------------------------------------------------------------------------- + vlan_workload_networks: + type: array + description: "Workload VLANs for customer workloads (up to 6 networks)" + required: false + minItems: 0 + maxItems: 6 + items: + type: object + properties: + vlan_id: + type: integer + minimum: 1 + maximum: 4094 + name: + type: string + cidr: + type: string + format: cidr + gateway: + type: string + format: ipv4 + dns_servers: + type: array + items: + type: string + format: ipv4 + dhcp_enabled: + type: boolean + routable: + type: boolean + default: true + example: + - vlan_id: 501 + name: "workload-web" + cidr: "192.168.100.0/24" + gateway: "192.168.100.1" + dns_servers: ["10.100.1.36", "10.100.1.37"] + dhcp_enabled: true + routable: true + - vlan_id: 502 + name: "workload-app" + cidr: "192.168.51.0/24" + gateway: "192.168.51.1" + dns_servers: ["10.100.1.36", "10.100.1.37"] + dhcp_enabled: true + routable: true + + vlan_workload_1_id: + type: integer + description: "Workload VLAN 1 ID" + required: false + minimum: 1 + maximum: 4094 + example: 501 + + vlan_workload_1_name: + type: string + description: "Workload VLAN 1 name" + required: false + example: "workload-web" + + vlan_workload_1_cidr: + type: string + description: "Workload VLAN 1 CIDR" + required: false + format: cidr + example: "192.168.100.0/24" + + vlan_workload_1_gateway: + type: string + description: "Workload VLAN 1 gateway" + required: false + format: ipv4 + example: "192.168.100.1" + + vlan_workload_2_id: + type: integer + description: "Workload VLAN 2 ID" + required: false + minimum: 1 + maximum: 4094 + example: 502 + + vlan_workload_2_name: + type: string + description: "Workload VLAN 2 name" + required: false + example: "workload-app" + + vlan_workload_2_cidr: + type: string + description: "Workload VLAN 2 CIDR" + required: false + format: cidr + example: "192.168.51.0/24" + + vlan_workload_2_gateway: + type: string + description: "Workload VLAN 2 gateway" + required: false + format: ipv4 + example: "192.168.51.1" + + vlan_workload_3_id: + type: integer + description: "Workload VLAN 3 ID" + required: false + minimum: 1 + maximum: 4094 + example: 503 + + vlan_workload_3_name: + type: string + description: "Workload VLAN 3 name" + required: false + example: "workload-data" + + vlan_workload_3_cidr: + type: string + description: "Workload VLAN 3 CIDR" + required: false + format: cidr + example: "192.168.52.0/24" + + vlan_workload_3_gateway: + type: string + description: "Workload VLAN 3 gateway" + required: false + format: ipv4 + example: "192.168.52.1" + + vlan_workload_4_id: + type: integer + description: "Workload VLAN 4 ID" + required: false + minimum: 1 + maximum: 4094 + example: 504 + + vlan_workload_4_name: + type: string + description: "Workload VLAN 4 name" + required: false + example: "workload-dmz" + + vlan_workload_4_cidr: + type: string + description: "Workload VLAN 4 CIDR" + required: false + format: cidr + example: "192.168.53.0/24" + + vlan_workload_4_gateway: + type: string + description: "Workload VLAN 4 gateway" + required: false + format: ipv4 + example: "192.168.53.1" + + vlan_workload_5_id: + type: integer + description: "Workload VLAN 5 ID" + required: false + minimum: 1 + maximum: 4094 + example: 505 + + vlan_workload_5_name: + type: string + description: "Workload VLAN 5 name" + required: false + example: "workload-test" + + vlan_workload_5_cidr: + type: string + description: "Workload VLAN 5 CIDR" + required: false + format: cidr + example: "192.168.54.0/24" + + vlan_workload_5_gateway: + type: string + description: "Workload VLAN 5 gateway" + required: false + format: ipv4 + example: "192.168.54.1" + + vlan_workload_6_id: + type: integer + description: "Workload VLAN 6 ID" + required: false + minimum: 1 + maximum: 4094 + example: 506 + + vlan_workload_6_name: + type: string + description: "Workload VLAN 6 name" + required: false + example: "workload-dev" + + vlan_workload_6_cidr: + type: string + description: "Workload VLAN 6 CIDR" + required: false + format: cidr + example: "192.168.55.0/24" + + vlan_workload_6_gateway: + type: string + description: "Workload VLAN 6 gateway" + required: false + format: ipv4 + example: "192.168.55.1" + +# ============================================================================= +# ON-PREM NETWORKING - DHCP +# ============================================================================= + +dhcp: + infrastructure_type: ["azure_local", "wsfc_s2d", "wsfc_san"] + # DHCP SERVICE CONFIGURATION (Platform-Agnostic) + # --------------------------------------------------------------------------- + dhcp_service_enabled: + type: boolean + description: "Enable DHCP service globally" + required: false + default: true + + dhcp_service_platform: + type: string + description: "DHCP service platform" + required: false + options: + - "windows_server" + - "fortigate" + - "cisco_ios" + - "linux_isc_dhcp" + - "infoblox" + example: "fortigate" + + dhcp_server_ip: + type: string + description: "DHCP server IP address (for relay/helper configuration)" + required: false + format: ipv4 + example: "192.168.211.5" + + # DHCP SCOPES (Array-Based, Multiple Scopes per Network) + # --------------------------------------------------------------------------- + dhcp_scopes: + type: array + description: "DHCP scopes for all networks (platform-agnostic)" + required: false + items: + type: object + properties: + scope_name: + type: string + vlan_id: + type: integer + network: + type: string + format: cidr + subnet_mask: + type: string + format: ipv4 + gateway: + type: string + format: ipv4 + range_start: + type: string + format: ipv4 + range_end: + type: string + format: ipv4 + dns_servers: + type: array + items: + type: string + format: ipv4 + domain_name: + type: string + ntp_servers: + type: array + items: + type: string + format: ipv4 + lease_time: + type: integer + enabled: + type: boolean + reservations: + type: array + items: + type: object + properties: + mac_address: + type: string + ip_address: + type: string + format: ipv4 + hostname: + type: string + exclusions: + type: array + items: + type: object + properties: + start_ip: + type: string + format: ipv4 + end_ip: + type: string + format: ipv4 + example: + - scope_name: "management-compute" + vlan_id: 211 + network: "192.168.211.0/24" + subnet_mask: "255.255.255.0" + gateway: "192.168.211.1" + range_start: "192.168.211.100" + range_end: "192.168.211.200" + dns_servers: ["10.100.1.36", "10.100.1.37"] + domain_name: "azlocal.cloud" + ntp_servers: ["10.100.1.36"] + lease_time: 86400 + enabled: true + reservations: + - mac_address: "00:15:5D:00:00:01" + ip_address: "192.168.211.10" + hostname: "azlc-node01" + exclusions: + - start_ip: "192.168.211.1" + end_ip: "192.168.211.50" + - scope_name: "workload-web" + vlan_id: 501 + network: "192.168.100.0/24" + subnet_mask: "255.255.255.0" + gateway: "192.168.100.1" + range_start: "192.168.100.100" + range_end: "192.168.100.200" + dns_servers: ["10.100.1.36", "10.100.1.37"] + domain_name: "azlocal.cloud" + lease_time: 43200 + enabled: true + + # MANAGEMENT-COMPUTE DHCP SCOPE + # --------------------------------------------------------------------------- + dhcp_management_compute_enabled: + type: boolean + description: "Enable DHCP for management-compute network" + required: false + default: true + + dhcp_management_compute_range_start: + type: string + description: "Management-compute DHCP range start" + required: false + format: ipv4 + example: "192.168.211.100" + + dhcp_management_compute_range_end: + type: string + description: "Management-compute DHCP range end" + required: false + format: ipv4 + example: "192.168.211.200" + + dhcp_management_compute_lease_time: + type: integer + description: "Management-compute DHCP lease time (seconds)" + required: false + minimum: 300 + maximum: 31536000 + default: 86400 + + dhcp_management_compute_dns_servers: + type: array + description: "Management-compute DHCP DNS servers" + required: false + items: + type: string + format: ipv4 + example: + - "10.100.1.36" + - "10.100.1.37" + + dhcp_management_compute_domain: + type: string + description: "Management-compute DHCP domain name" + required: false + example: "azlocal.cloud" + + dhcp_management_compute_ntp_servers: + type: array + description: "Management-compute DHCP NTP servers" + required: false + items: + type: string + format: ipv4 + example: + - "10.100.1.36" + + dhcp_management_compute_exclusions: + type: array + description: "Management-compute DHCP exclusion ranges" + required: false + items: + type: object + properties: + start_ip: + type: string + format: ipv4 + end_ip: + type: string + format: ipv4 + example: + - start_ip: "192.168.211.1" + end_ip: "192.168.211.50" # OUT-OF-BAND DHCP SCOPE + # --------------------------------------------------------------------------- + dhcp_oob_enabled: + type: boolean + description: "Enable DHCP for out-of-band network" + required: false + default: true + + dhcp_oob_range_start: + type: string + description: "Out-of-band DHCP range start" + required: false + format: ipv4 + example: "192.168.210.100" + + dhcp_oob_range_end: + type: string + description: "Out-of-band DHCP range end" + required: false + format: ipv4 + example: "192.168.210.200" + + dhcp_oob_lease_time: + type: integer + description: "Out-of-band DHCP lease time (seconds)" + required: false + minimum: 300 + maximum: 31536000 + default: 86400 + + dhcp_oob_dns_servers: + type: array + description: "Out-of-band DHCP DNS servers" + required: false + items: + type: string + format: ipv4 + example: + - "8.8.8.8" + - "8.8.4.4" + + # WORKLOAD DHCP SCOPES (1-6) + # --------------------------------------------------------------------------- + dhcp_workload_1_enabled: + type: boolean + description: "Enable DHCP for workload network 1" + required: false + default: false + + dhcp_workload_1_range_start: + type: string + description: "Workload 1 DHCP range start" + required: false + format: ipv4 + example: "192.168.100.100" + + dhcp_workload_1_range_end: + type: string + description: "Workload 1 DHCP range end" + required: false + format: ipv4 + example: "192.168.100.200" + + dhcp_workload_1_lease_time: + type: integer + description: "DHCP lease duration in seconds for workload network 1 (recommended: 86400 for static devices, 3600 for dynamic clients)" + required: false + minimum: 600 + maximum: 2592000 + default: 43200 + note: "Default 43200 (12 hours). Min 600 (10 min), Max 2592000 (30 days)" + + dhcp_workload_2_enabled: + type: boolean + description: "Enable DHCP for workload network 2" + required: false + default: false + + dhcp_workload_2_range_start: + type: string + description: "Workload 2 DHCP range start" + required: false + format: ipv4 + example: "192.168.51.100" + + dhcp_workload_2_range_end: + type: string + description: "Workload 2 DHCP range end" + required: false + format: ipv4 + example: "192.168.51.200" + + dhcp_workload_2_lease_time: + type: integer + description: "DHCP lease duration in seconds for workload network 2 (recommended: 86400 for static devices, 3600 for dynamic clients)" + required: false + minimum: 600 + maximum: 2592000 + default: 43200 + note: "Default 43200 (12 hours). Min 600 (10 min), Max 2592000 (30 days)" + + dhcp_workload_3_enabled: + type: boolean + description: "Enable DHCP for workload network 3" + required: false + default: false + + dhcp_workload_3_range_start: + type: string + description: "Workload 3 DHCP range start" + required: false + format: ipv4 + example: "192.168.52.100" + + dhcp_workload_3_range_end: + type: string + description: "Workload 3 DHCP range end" + required: false + format: ipv4 + example: "192.168.52.200" + + dhcp_workload_3_lease_time: + type: integer + description: "DHCP lease duration in seconds for workload network 3 (recommended: 86400 for static devices, 3600 for dynamic clients)" + required: false + minimum: 600 + maximum: 2592000 + default: 43200 + note: "Default 43200 (12 hours). Min 600 (10 min), Max 2592000 (30 days)" + + dhcp_workload_4_enabled: + type: boolean + description: "Enable DHCP for workload network 4" + required: false + default: false + + dhcp_workload_4_range_start: + type: string + description: "Workload 4 DHCP range start" + required: false + format: ipv4 + example: "192.168.53.100" + + dhcp_workload_4_range_end: + type: string + description: "Workload 4 DHCP range end" + required: false + format: ipv4 + example: "192.168.53.200" + + dhcp_workload_4_lease_time: + type: integer + description: "DHCP lease duration in seconds for workload network 4 (recommended: 86400 for static devices, 3600 for dynamic clients)" + required: false + minimum: 600 + maximum: 2592000 + default: 43200 + note: "Default 43200 (12 hours). Min 600 (10 min), Max 2592000 (30 days)" + + dhcp_workload_5_enabled: + type: boolean + description: "Enable DHCP for workload network 5" + required: false + default: false + + dhcp_workload_5_range_start: + type: string + description: "Workload 5 DHCP range start" + required: false + format: ipv4 + example: "192.168.54.100" + + dhcp_workload_5_range_end: + type: string + description: "Workload 5 DHCP range end" + required: false + format: ipv4 + example: "192.168.54.200" + + dhcp_workload_5_lease_time: + type: integer + description: "DHCP lease duration in seconds for workload network 5 (recommended: 86400 for static devices, 3600 for dynamic clients)" + required: false + minimum: 600 + maximum: 2592000 + default: 43200 + note: "Default 43200 (12 hours). Min 600 (10 min), Max 2592000 (30 days)" + + dhcp_workload_6_enabled: + type: boolean + description: "Enable DHCP for workload network 6" + required: false + default: false + + dhcp_workload_6_range_start: + type: string + description: "Workload 6 DHCP range start" + required: false + format: ipv4 + example: "192.168.55.100" + + dhcp_workload_6_range_end: + type: string + description: "Workload 6 DHCP range end" + required: false + format: ipv4 + example: "192.168.55.200" + + dhcp_workload_6_lease_time: + type: integer + description: "DHCP lease duration in seconds for workload network 6 (recommended: 86400 for static devices, 3600 for dynamic clients)" + required: false + minimum: 600 + maximum: 2592000 + default: 43200 + note: "Default 43200 (12 hours). Min 600 (10 min), Max 2592000 (30 days)" + + # DHCP RESERVATIONS (Global) + # --------------------------------------------------------------------------- + dhcp_reservations: + type: array + description: "DHCP reservations across all scopes" + required: false + items: + type: object + properties: + scope_name: + type: string + mac_address: + type: string + pattern: "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$" + ip_address: + type: string + format: ipv4 + hostname: + type: string + description: + type: string + example: + - scope_name: "management-compute" + mac_address: "00:15:5D:00:00:01" + ip_address: "192.168.211.10" + hostname: "azlc-node01" + description: "Azure Local Node 1" + - scope_name: "management-compute" + mac_address: "00:15:5D:00:00:02" + ip_address: "192.168.211.11" + hostname: "azlc-node02" + description: "Azure Local Node 2" # DHCP OPTIONS (Global) + # --------------------------------------------------------------------------- + dhcp_option_domain_name: + type: string + description: "DHCP option 15 - Domain name" + required: false + example: "azlocal.cloud" + + dhcp_option_tftp_server: + type: string + description: "DHCP option 66 - TFTP server (for PXE boot)" + required: false + format: ipv4 + example: "192.168.211.5" + + dhcp_option_bootfile_name: + type: string + description: "DHCP option 67 - Bootfile name (for PXE boot)" + required: false + example: "pxeboot.n12" + + dhcp_relay_agent_enabled: + type: boolean + description: "Enable DHCP relay agent (IP helper)" + required: false + default: false + + dhcp_relay_server_ips: + type: array + description: "DHCP relay server IP addresses" + required: false + items: + type: string + format: ipv4 + example: + - "192.168.211.5" + + +# ============================================================================= +# CLUSTERS (ORCHESTRATION & MANAGEMENT) +# ============================================================================= +# Parent section for cluster orchestration technologies. Contains both Azure +# Local (modern hyper-converged infrastructure) and Windows Server Failover +# Clustering (traditional clustering) configurations. +# +# CHILD SECTIONS: +# - azure_local: Azure Local clusters (formerly Azure Stack HCI) +# - wsfc: Windows Server Failover Clustering +# +# SHARED INFRASTRUCTURE: +# - cluster_nodes: (line 5218+) Generic physical node configurations used by both technologies + +clusters: + + # =========================================================================== + # AZURE LOCAL CLUSTER + # =========================================================================== + # Azure Local (formerly Azure Stack HCI) is Microsoft's hyper-converged + # infrastructure solution that extends Azure services to on-premises + # environments. Supports 1-16 node clusters with Storage Spaces Direct. + + azure_local: + infrastructure_type: ["azure_local"] + # CLUSTER IDENTIFICATION + # ------------------------------------------------------------------------- + azl_name: + type: string + description: "Azure Local cluster name (NetBIOS name, max 15 characters)" + required: true + maxLength: 15 + pattern: "^[a-zA-Z][a-zA-Z0-9-]*$" + example: "azlc-clus01" + + azl_resource_group: + type: string + description: "Cluster resource group (function-based naming)" + required: true + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(landingzone|management)-cluster$" + example: "rg-azlc-iic-eus-landingzone-cluster" + + azl_subscription: + type: string + description: "Azure subscription ID for cluster resources" + required: true + format: uuid + example: "00000000-1111-2222-3333-444444444444" + + azl_location: + type: string + description: "Azure region for cluster registration" + required: true + allowedValues: + - "East US" + - "West Europe" + - "Australia East" + - "Southeast Asia" + - "India Central" + - "Canada Central" + - "Japan East" + - "South Central US" + example: "East US" + + azl_rack: + type: string + description: "Physical rack location identifier" + required: false + example: "RAL-R001" + + azl_environment: + type: string + description: "Deployment environment type" + required: false + allowedValues: + - production + - development + - testing + - staging + default: "production" + + azl_custom_location_name: + category: "azure-local" + subcategory: "cluster-config" + type: string + description: "Custom location name for Arc resource bridge" + required: false + example: "azlc-custom-location" + + # CLUSTER SIZE AND CONFIGURATION + # --------------------------------------------------------------------------- + azl_node_count: + category: "azure-local" + subcategory: "cluster-size" + type: integer + description: "Number of nodes in the cluster (Microsoft sweet spot: 6 nodes)" + required: true + minimum: 1 + maximum: 16 + example: 6 + + # STORAGE TOPOLOGY (Switchless vs Switched) + # --------------------------------------------------------------------------- + azl_storage_topology: + category: "azure-local" + subcategory: "storage-topology" + type: string + description: "Storage network topology (switchless: 1-4 nodes only, switched: 4-16 nodes, scale-out requires switched)" + required: true + allowedValues: + - switchless + - switched + example: "switched" + + azl_storage_switchless_node_limit: + type: integer + description: "Maximum nodes for switchless storage (read-only reference)" + required: false + minimum: 1 + maximum: 4 + default: 4 + + azl_storage_rdma_enabled: + type: boolean + description: "Enable RDMA for storage traffic (requires 10Gbps+ adapters)" + required: false + default: true + + azl_storage_rdma_technology: + type: string + description: "RDMA technology for storage" + required: false + allowedValues: + - RoCEv2 + - iWARP + default: "RoCEv2" + + # IDENTITY PROVIDER (Active Directory vs Local Identity with Key Vault) + # --------------------------------------------------------------------------- + azl_identity_provider: + type: string + description: "Identity provider (active_directory: use active_directory section, local_identity: use per-cluster Key Vault)" + required: true + allowedValues: + - active_directory + - local_identity + example: "active_directory" + + # NOTE: For active_directory provider, use variables from active_directory: section (ad_domain_fqdn, ad_ou_path) + + # Local Identity with Key Vault Settings + azl_local_identity_key_vault_name: + category: "security" + subcategory: "keyvault" + type: string + description: "Key Vault name for local identity secrets (required for local_identity provider, one per cluster)" + required: false + depends_on: + - variable: "azl_identity_provider" + condition: "equals: local_identity" + message: "Key Vault name required when using local_identity provider" + minLength: 3 + maxLength: 24 + pattern: "^[a-z][a-z0-9]+(-[a-z0-9]+)*$" + note: "No consecutive hyphens allowed" + example: "kv-azlc-cluster01" + + azl_local_identity_key_vault_resource_group: + type: string + description: "Resource group for Key Vault (function-based naming: identity or management function)" + required: false + depends_on: + - variable: "azl_identity_provider" + condition: "equals: local_identity" + message: "Key Vault resource group required when using local_identity provider" + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(identity|management)-keyvault$" + example: "rg-azlc-iic-eus-identity-keyvault" + + azl_local_identity_recovery_admin_username: + type: string + description: "Recovery admin username for local identity deployments (password stored in Key Vault)" + required: false + depends_on: + - variable: "azl_identity_provider" + condition: "equals: local_identity" + message: "Recovery admin username only applies to local_identity provider" + default: "RecoveryAdmin" + + azl_local_identity_zone_name: + type: string + description: "DNS zone name for local identity deployment (must be resolvable internally or externally)" + required: false + depends_on: + - variable: "azl_identity_provider" + condition: "equals: local_identity" + message: "DNS zone name required when using local_identity provider" + example: "azlocal.cloud" + + # NOTE: For network intents, use network_intents: array section (line 5578) + + # CLUSTER WITNESS (Quorum Configuration) + # --------------------------------------------------------------------------- + azl_witness_type: + type: string + description: "Cluster witness type (cloud_witness: Azure Storage, file_share_witness: SMB share, disk_witness: shared disk)" + required: true + allowedValues: + - cloud_witness + - file_share_witness + - disk_witness + default: "cloud_witness" + + # File Share Witness Configuration + azl_witness_file_share_path: + type: string + description: "File share path for witness (UNC path, minimum 5MB free space)" + required: false + format: unc_path + example: "\\\\fileserver.azlocal.cloud\\witness" + + # Disk Witness Configuration (not supported for Storage Spaces Direct) + azl_witness_disk_resource: + type: string + description: "Disk resource name for witness (not supported with Storage Spaces Direct)" + required: false + example: "Cluster Disk 3" + + # AZURE ARC AND CONNECTIVITY + # --------------------------------------------------------------------------- + azl_arc_gateway_enabled: + type: boolean + description: "Use Arc gateway for proxy/restricted environments" + required: false + default: false + + azl_arc_gateway_name: + category: "azure-local" + subcategory: "arc-gateway" + type: string + description: "Arc gateway name (if using Arc gateway)" + required: false + depends_on: + - variable: "azl_arc_gateway_enabled" + condition: "equals: true" + message: "Arc gateway name required when Arc gateway is enabled" + example: "azlc-arc-gateway" + + azl_arc_gateway_resource_group: + type: string + description: "Arc gateway resource group (function-based naming: connectivity function)" + required: false + depends_on: + - variable: "azl_arc_gateway_enabled" + condition: "equals: true" + message: "Arc gateway resource group required when Arc gateway is enabled" + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(connectivity|management)-arcgw$" + example: "rg-azlc-iic-eus-connectivity-arcgw" + + azl_arc_gateway_id: + type: string + description: "Arc gateway resource ID (required when azl_arc_gateway_enabled is true)" + required: false + depends_on: + - variable: "azl_arc_gateway_enabled" + condition: "equals: true" + message: "Arc gateway resource ID required when Arc gateway is enabled" + format: azure_resource_id + example: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-connectivity-arcgw/providers/Microsoft.HybridCompute/gateways/azlc-arc-gateway" + + # STORAGE SPACES DIRECT PHYSICAL DRIVES + # --------------------------------------------------------------------------- + # Physical storage configuration for Storage Spaces Direct (S2D). + # Azure storage accounts for witness and diagnostics are in the storage: section. + + # Cache Tier Configuration + azl_cache_drive_type: + type: string + description: "Physical cache drive type for Storage Spaces Direct" + required: false + allowedValues: + - SSD + - NVMe + example: "SSD" + + azl_cache_drives_count: + type: integer + description: "Number of cache drives per node" + required: false + minimum: 1 + maximum: 16 + example: 2 + + azl_cache_drives_size_gb: + type: integer + description: "Size of each cache drive in GB" + required: false + minimum: 128 + maximum: 8192 + example: 960 + + # Capacity Tier Configuration + azl_capacity_drive_type: + type: string + description: "Physical capacity drive type for Storage Spaces Direct" + required: false + allowedValues: + - HDD + - SSD + - NVMe + example: "HDD" + + azl_capacity_drives_count: + type: integer + description: "Number of capacity drives per node" + required: false + minimum: 4 + maximum: 64 + example: 8 + + azl_capacity_drives_size_gb: + type: integer + description: "Size of each capacity drive in GB" + required: false + minimum: 1000 + maximum: 16384 + example: 4000 + + # =========================================================================== + # WINDOWS SERVER FAILOVER CLUSTER (WSFC) + # =========================================================================== + # Windows Server Failover Clustering for non-Azure Local deployments. + # Supports multiple storage architectures and Windows Server versions. + # + # STORAGE ARCHITECTURE OPTIONS: + # - S2D (Storage Spaces Direct): Hyper-converged storage using local drives + # - SAN: Traditional shared storage (iSCSI, Fibre Channel, FCoE) + # - Hybrid: Mix of local and shared storage + # + # SUPPORTED OS VERSIONS: + # - Windows Server 2019 (Build 17763) + # - Windows Server 2022 (Build 20348) + # - Windows Server 2025 (Build 26100) + # + # RELATED SECTIONS: + # - cluster_nodes: (line 5218+) - Physical node configurations + # - vlans: (line 3480+) - VLAN definitions for cluster networking + # - storage: (line 7800+) - Azure storage accounts for witness/diagnostics + # - failover_cluster: (line 8656+) - Legacy failover cluster variables + + wsfc: + infrastructure_type: ["wsfc_s2d", "wsfc_san"] + # ------------------------------------------------------------------------- + # CLUSTER IDENTIFICATION + # ------------------------------------------------------------------------- + wsfc_cluster_name: + type: string + description: "Windows Server Failover Cluster name (NetBIOS name, 15 chars max)" + required: true + minLength: 1 + maxLength: 15 + pattern: "^[a-zA-Z0-9-]+$" + example: "WSFC-CLUS01" + + wsfc_cluster_ip: + type: string + description: "Cluster IP address (static IP for cluster name object)" + required: true + format: ipv4 + example: "192.168.211.50" + + wsfc_domain_fqdn: + type: string + description: "Active Directory domain FQDN for cluster" + required: true + format: fqdn + example: "corp.Infinite Improbability Corp.com" + + wsfc_ou_path: + type: string + description: "Active Directory OU path for cluster objects" + required: false + format: ldap_dn + example: "OU=Clusters,OU=Servers,DC=corp,DC=Infinite Improbability Corp,DC=com" + + wsfc_description: + type: string + description: "Cluster description for documentation" + required: false + maxLength: 256 + example: "Production Hyper-V Failover Cluster" + + wsfc_environment: + type: string + description: "Environment type (production, staging, development, test)" + required: false + allowedValues: + - production + - staging + - development + - test + default: "production" + + # --------------------------------------------------------------------------- + # OPERATING SYSTEM VERSION + # --------------------------------------------------------------------------- + wsfc_os_version: + type: string + description: "Windows Server version for cluster nodes" + required: true + allowedValues: + - Server2019 + - Server2022 + - Server2025 + example: "Server2022" + references: + - url: "https://learn.microsoft.com/windows-server/get-started/editions-comparison" + + wsfc_os_build: + type: string + description: "Specific OS build number (optional, for tracking updates)" + required: false + pattern: "^[0-9]+\\.[0-9]+$" + example: "20348.2227" + + wsfc_os_edition: + type: string + description: "Windows Server edition (Datacenter required for S2D)" + required: true + allowedValues: + - Datacenter + - Standard + default: "Datacenter" + note: "Datacenter edition required for Storage Spaces Direct and unlimited VMs" + + wsfc_os_installation_type: + type: string + description: "Installation type (ServerCore recommended for production)" + required: false + allowedValues: + - ServerCore + - DesktopExperience + default: "ServerCore" + + wsfc_cluster_functional_level: + type: integer + description: "Cluster functional level (matches OS version: 2019=10, 2022=11, 2025=12)" + required: false + minimum: 10 + maximum: 12 + example: 11 + + # --------------------------------------------------------------------------- + # STORAGE ARCHITECTURE + # --------------------------------------------------------------------------- + wsfc_storage_type: + type: string + description: "Storage architecture type for cluster" + required: true + allowedValues: + - s2d + - san + - hybrid + example: "s2d" + + # STORAGE SPACES DIRECT (S2D) CONFIGURATION + # --------------------------------------------------------------------------- + wsfc_s2d_enabled: + type: boolean + description: "Enable Storage Spaces Direct (requires wsfc_storage_type = s2d)" + required: false + depends_on: + - variable: "wsfc_storage_type" + value: "s2d" + default: false + + wsfc_s2d_cache_mode: + type: string + description: "S2D cache tier behavior (ReadWrite: cache reads and writes, ReadOnly: cache reads only, WriteOnly: cache writes only)" + required: false + depends_on: + - variable: "wsfc_s2d_enabled" + value: true + allowedValues: + - ReadWrite + - ReadOnly + - WriteOnly + default: "ReadWrite" + + wsfc_s2d_cache_page_size_kbytes: + type: integer + description: "S2D cache page size in KB (8, 16, 32, or 64)" + required: false + depends_on: + - variable: "wsfc_s2d_enabled" + value: true + allowedValues: + - 8 + - 16 + - 32 + - 64 + default: 16 + + wsfc_s2d_resiliency_default: + type: string + description: "Default resiliency setting for S2D volumes" + required: false + depends_on: + - variable: "wsfc_s2d_enabled" + value: true + allowedValues: + - Mirror + - Parity + - MirrorAcceleratedParity + default: "Mirror" + + wsfc_s2d_mirror_resiliency_type: + type: string + description: "Mirror resiliency type (TwoWay: 2-copy mirror, ThreeWay: 3-copy mirror)" + required: false + depends_on: + - variable: "wsfc_s2d_enabled" + value: true + allowedValues: + - TwoWay + - ThreeWay + default: "TwoWay" + note: "ThreeWay requires minimum 5 nodes" + + wsfc_s2d_filesystem: + type: string + description: "Default filesystem for S2D volumes (ReFS recommended for S2D)" + required: false + depends_on: + - variable: "wsfc_s2d_enabled" + value: true + allowedValues: + - ReFS + - NTFS + default: "ReFS" + + wsfc_s2d_cache_metadata_reserve_gb: + type: integer + description: "Cache metadata reserve in GB (1-100)" + required: false + depends_on: + - variable: "wsfc_s2d_enabled" + value: true + minimum: 1 + maximum: 100 + default: 1 + + # STORAGE SPACES DIRECT - PHYSICAL DRIVES + # --------------------------------------------------------------------------- + wsfc_s2d_cache_drive_type: + type: string + description: "Physical cache drive type for S2D" + required: false + depends_on: + - variable: "wsfc_s2d_enabled" + value: true + allowedValues: + - SSD + - NVMe + example: "SSD" + + wsfc_s2d_cache_drives_per_node: + type: integer + description: "Number of cache drives per node" + required: false + depends_on: + - variable: "wsfc_s2d_enabled" + value: true + minimum: 1 + maximum: 16 + example: 2 + + wsfc_s2d_cache_drive_size_gb: + type: integer + description: "Size of each cache drive in GB" + required: false + depends_on: + - variable: "wsfc_s2d_enabled" + value: true + minimum: 128 + maximum: 8192 + example: 960 + + wsfc_s2d_capacity_drive_type: + type: string + description: "Physical capacity drive type for S2D" + required: false + depends_on: + - variable: "wsfc_s2d_enabled" + value: true + allowedValues: + - HDD + - SSD + - NVMe + example: "HDD" + + wsfc_s2d_capacity_drives_per_node: + type: integer + description: "Number of capacity drives per node" + required: false + depends_on: + - variable: "wsfc_s2d_enabled" + value: true + minimum: 4 + maximum: 64 + example: 8 + + wsfc_s2d_capacity_drive_size_gb: + type: integer + description: "Size of each capacity drive in GB" + required: false + depends_on: + - variable: "wsfc_s2d_enabled" + value: true + minimum: 1000 + maximum: 16384 + example: 4000 + + # SAN STORAGE CONFIGURATION + # --------------------------------------------------------------------------- + wsfc_san_enabled: + type: boolean + description: "Enable SAN/shared storage (requires wsfc_storage_type = san)" + required: false + depends_on: + - variable: "wsfc_storage_type" + value: "san" + default: false + + wsfc_san_connection_type: + type: string + description: "SAN connection type" + required: false + depends_on: + - variable: "wsfc_san_enabled" + value: true + allowedValues: + - iscsi + - fibre_channel + - fcoe + example: "iscsi" + + wsfc_san_vendor: + type: string + description: "SAN vendor/manufacturer" + required: false + depends_on: + - variable: "wsfc_san_enabled" + value: true + example: "Dell EMC" + + wsfc_san_model: + type: string + description: "SAN model/array type" + required: false + depends_on: + - variable: "wsfc_san_enabled" + value: true + example: "PowerStore 500T" + + wsfc_san_mpio_enabled: + type: boolean + description: "Enable Multipath I/O (MPIO) for SAN connections" + required: false + depends_on: + - variable: "wsfc_san_enabled" + value: true + default: true + + wsfc_san_mpio_policy: + type: string + description: "MPIO load balancing policy" + required: false + depends_on: + - variable: "wsfc_san_mpio_enabled" + value: true + allowedValues: + - RoundRobin + - LeastQueueDepth + - WeightedPaths + - FailOver + default: "RoundRobin" + + # iSCSI Configuration + wsfc_san_iscsi_target_portal: + type: string + description: "iSCSI target portal IP address" + required: false + depends_on: + - variable: "wsfc_san_connection_type" + value: "iscsi" + format: ipv4 + example: "192.168.100.10" + + wsfc_san_iscsi_target_iqn: + type: string + description: "iSCSI target IQN (iSCSI Qualified Name)" + required: false + depends_on: + - variable: "wsfc_san_connection_type" + value: "iscsi" + pattern: "^iqn\\.[0-9]{4}-[0-9]{2}\\.[a-z0-9.-]+:[a-z0-9.-]+$" + example: "iqn.1992-04.com.emc:storage.array.12345" + + wsfc_san_iscsi_port: + type: integer + description: "iSCSI target port (default 3260)" + required: false + depends_on: + - variable: "wsfc_san_connection_type" + value: "iscsi" + default: 3260 + + wsfc_san_iscsi_chap_enabled: + type: boolean + description: "Enable CHAP authentication for iSCSI" + required: false + depends_on: + - variable: "wsfc_san_connection_type" + value: "iscsi" + default: true + + # Fibre Channel Configuration + wsfc_san_fc_hba_count: + type: integer + description: "Number of Fibre Channel HBAs per node" + required: false + depends_on: + - variable: "wsfc_san_connection_type" + value: "fibre_channel" + minimum: 1 + maximum: 4 + example: 2 + + wsfc_san_fc_fabric_type: + type: string + description: "Fibre Channel fabric topology" + required: false + depends_on: + - variable: "wsfc_san_connection_type" + value: "fibre_channel" + allowedValues: + - single_fabric + - dual_fabric + default: "dual_fabric" + + # SAN LUN Configuration + wsfc_san_luns: + type: array + description: "Array of SAN LUNs for cluster storage" + required: false + depends_on: + - variable: "wsfc_san_enabled" + value: true + items: + type: object + properties: + lun_id: + type: integer + description: "LUN ID" + lun_name: + type: string + description: "LUN name/label" + size_gb: + type: integer + description: "LUN size in GB" + purpose: + type: string + description: "LUN purpose (quorum, csv, vm_storage)" + example: + - lun_id: 0 + lun_name: "cluster-quorum" + size_gb: 10 + purpose: "quorum" + - lun_id: 1 + lun_name: "csv-volume1" + size_gb: 2000 + purpose: "csv" + + # --------------------------------------------------------------------------- + # CLUSTER NETWORKING + # --------------------------------------------------------------------------- + wsfc_network_priority: + type: array + description: "Network priority order for cluster communications (1=highest)" + required: false + items: + type: string + example: + - "Management-Compute" + - "Storage-1" + + wsfc_csv_network: + type: string + description: "Preferred network for CSV traffic" + required: false + example: "Storage-1" + + wsfc_live_migration_network: + type: string + description: "Preferred network for live migration traffic" + required: false + example: "Storage-1" + + wsfc_network_encryption: + type: boolean + description: "Enable cluster network encryption (Windows Server 2022+)" + required: false + default: false + + # --------------------------------------------------------------------------- + # CLUSTER SHARED VOLUMES (CSV) + # --------------------------------------------------------------------------- + wsfc_csv_enabled: + type: boolean + description: "Enable Cluster Shared Volumes" + required: false + default: true + + wsfc_csv_cache_enabled: + type: boolean + description: "Enable CSV in-memory read cache" + required: false + depends_on: + - variable: "wsfc_csv_enabled" + value: true + default: false + + wsfc_csv_cache_size_mb: + type: integer + description: "CSV cache size in MB (512-8192)" + required: false + depends_on: + - variable: "wsfc_csv_cache_enabled" + value: true + minimum: 512 + maximum: 8192 + default: 1024 + + wsfc_csv_block_cache: + type: boolean + description: "Enable CSV block cache for ReFS volumes (Windows Server 2016+)" + required: false + depends_on: + - variable: "wsfc_csv_enabled" + value: true + default: true + + wsfc_csv_volumes: + type: array + description: "Array of CSV volume configurations" + required: false + depends_on: + - variable: "wsfc_csv_enabled" + value: true + items: + type: object + properties: + volume_name: + type: string + description: "CSV volume name" + size_gb: + type: integer + description: "Volume size in GB" + filesystem: + type: string + description: "Filesystem (ReFS or NTFS)" + resiliency: + type: string + description: "S2D resiliency type (Mirror, Parity)" + example: + - volume_name: "CSV01" + size_gb: 2000 + filesystem: "ReFS" + resiliency: "Mirror" + + # --------------------------------------------------------------------------- + # QUORUM CONFIGURATION + # --------------------------------------------------------------------------- + wsfc_quorum_type: + type: string + description: "Cluster quorum configuration type" + required: true + allowedValues: + - node_majority + - node_and_disk_majority + - node_and_file_share_majority + - cloud_witness + default: "cloud_witness" + + # Disk Witness (not supported with S2D) + wsfc_quorum_disk_resource: + type: string + description: "Disk witness resource name (not supported with S2D)" + required: false + depends_on: + - variable: "wsfc_quorum_type" + value: "node_and_disk_majority" + example: "Cluster Disk 1" + + wsfc_quorum_disk_size_gb: + type: integer + description: "Disk witness size in GB (minimum 512MB)" + required: false + depends_on: + - variable: "wsfc_quorum_type" + value: "node_and_disk_majority" + minimum: 1 + default: 1 + + # File Share Witness + wsfc_quorum_file_share_path: + type: string + description: "File share witness UNC path" + required: false + depends_on: + - variable: "wsfc_quorum_type" + value: "node_and_file_share_majority" + format: unc_path + example: "\\\\fileserver.corp.Infinite Improbability Corp.com\\witness" + + # Cloud Witness (Azure Storage Account) + wsfc_quorum_cloud_witness_account: + type: string + description: "Azure Storage Account name for cloud witness" + required: false + depends_on: + - variable: "wsfc_quorum_type" + value: "cloud_witness" + pattern: "^[a-z0-9]{3,24}$" + example: "stwsfcprodeus001" + + wsfc_quorum_cloud_witness_key: + type: string + description: "Azure Storage Account access key (sensitive)" + required: false + depends_on: + - variable: "wsfc_quorum_type" + value: "cloud_witness" + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-wsfc-mgmt/cloud-witness-key" + + wsfc_quorum_cloud_witness_endpoint: + type: string + description: "Azure Storage endpoint (for Azure Government or China clouds)" + required: false + depends_on: + - variable: "wsfc_quorum_type" + value: "cloud_witness" + format: url + default: "core.windows.net" + example: "core.usgovcloudapi.net" + + # --------------------------------------------------------------------------- + # CLUSTER ROLES AND SERVICES + # --------------------------------------------------------------------------- + wsfc_hyperv_role_enabled: + type: boolean + description: "Enable Hyper-V role on cluster nodes" + required: false + default: false + + # Hyper-V Virtual Switch Configuration (when wsfc_hyperv_role_enabled = true) + wsfc_virtual_switch_name: + type: string + description: "Name of the Hyper-V virtual switch for VM connectivity" + required: false + depends_on: + - variable: "wsfc_hyperv_role_enabled" + value: true + pattern: "^[a-zA-Z0-9-_]{1,64}$" + default: "DefaultSwitch" + example: "External-vSwitch-01" + + wsfc_virtual_switch_type: + type: string + description: "Type of virtual switch (External, Internal, Private)" + required: false + depends_on: + - variable: "wsfc_hyperv_role_enabled" + value: true + allowedValues: + - External + - Internal + - Private + default: "External" + + # Hyper-V Live Migration Configuration + wsfc_live_migration_enabled: + type: boolean + description: "Enable live migration for VMs" + required: false + depends_on: + - variable: "wsfc_hyperv_role_enabled" + value: true + default: true + + wsfc_live_migration_auth: + type: string + description: "Authentication protocol for live migration (Kerberos recommended for security)" + required: false + depends_on: + - variable: "wsfc_hyperv_role_enabled" + value: true + allowedValues: + - CredSSP + - Kerberos + default: "Kerberos" + + wsfc_live_migration_performance: + type: string + description: "Network protocol for live migration performance (SMB provides best performance with RDMA)" + required: false + depends_on: + - variable: "wsfc_hyperv_role_enabled" + value: true + allowedValues: + - TCP/IP + - Compression + - SMB + default: "SMB" + + # Hyper-V Advanced Host Settings + wsfc_numa_spanning: + type: boolean + description: "Allow VMs to span multiple NUMA nodes (enable only if workload requires more memory than single NUMA node)" + required: false + depends_on: + - variable: "wsfc_hyperv_role_enabled" + value: true + default: false + + wsfc_storage_qos_enabled: + type: boolean + description: "Enable Storage Quality of Service for IOPS management" + required: false + depends_on: + - variable: "wsfc_hyperv_role_enabled" + value: true + default: true + + wsfc_sofs_role_enabled: + type: boolean + description: "Enable Scale-Out File Server (SOFS) role" + required: false + default: false + + wsfc_sofs_name: + type: string + description: "SOFS cluster role name" + required: false + depends_on: + - variable: "wsfc_sofs_role_enabled" + value: true + maxLength: 15 + example: "SOFS01" + + wsfc_sofs_ip: + type: string + description: "SOFS IP address" + required: false + depends_on: + - variable: "wsfc_sofs_role_enabled" + value: true + format: ipv4 + example: "192.168.211.60" + + # --------------------------------------------------------------------------- + # CLUSTER HEALTH AND MONITORING + # --------------------------------------------------------------------------- + wsfc_health_check_interval: + type: integer + description: "Health check interval in seconds (default 60)" + required: false + minimum: 10 + maximum: 300 + default: 60 + + wsfc_drain_on_shutdown: + type: boolean + description: "Automatically drain cluster node roles on shutdown" + required: false + default: true + + wsfc_quarantine_enabled: + type: boolean + description: "Enable automatic quarantine for misbehaving nodes" + required: false + default: true + + wsfc_quarantine_threshold: + type: integer + description: "Number of failures before quarantine (default 3)" + required: false + minimum: 1 + maximum: 10 + default: 3 + + wsfc_cluster_log_level: + type: integer + description: "Cluster diagnostic log verbosity (1-5, 3=default)" + required: false + minimum: 1 + maximum: 5 + default: 3 + + wsfc_cluster_log_size_mb: + type: integer + description: "Maximum cluster log file size in MB" + required: false + minimum: 8 + maximum: 1024 + default: 300 + + # --------------------------------------------------------------------------- + # SECURITY SETTINGS + # --------------------------------------------------------------------------- + wsfc_smb_signing_enforced: + type: boolean + description: "Enforce SMB signing for cluster communications" + required: false + default: true + + wsfc_smb_encryption_enabled: + type: boolean + description: "Enable SMB encryption for CSV traffic (Windows Server 2012 R2+)" + required: false + default: false + + wsfc_bitlocker_csv: + type: boolean + description: "Enable BitLocker encryption on CSV volumes" + required: false + default: false + + wsfc_credential_guard: + type: boolean + description: "Enable Credential Guard on cluster nodes (Windows Server 2016+)" + required: false + default: false + + # --------------------------------------------------------------------------- + # PERFORMANCE TUNING + # --------------------------------------------------------------------------- + wsfc_same_subnet_delay: + type: integer + description: "Heartbeat delay for same subnet in milliseconds (default 1000)" + required: false + minimum: 250 + maximum: 5000 + default: 1000 + + wsfc_same_subnet_threshold: + type: integer + description: "Heartbeat threshold for same subnet (default 10)" + required: false + minimum: 3 + maximum: 20 + default: 10 + + wsfc_cross_subnet_delay: + type: integer + description: "Heartbeat delay for cross subnet in milliseconds (default 1000)" + required: false + minimum: 250 + maximum: 5000 + default: 1000 + + wsfc_cross_subnet_threshold: + type: integer + description: "Heartbeat threshold for cross subnet (default 20)" + required: false + minimum: 3 + maximum: 40 + default: 20 + + # --------------------------------------------------------------------------- + # CLUSTER MAINTENANCE + # --------------------------------------------------------------------------- + wsfc_cau_enabled: + type: boolean + description: "Enable Cluster-Aware Updating (CAU)" + required: false + default: false + + wsfc_cau_schedule: + type: string + description: "CAU maintenance window schedule (cron format)" + required: false + depends_on: + - variable: "wsfc_cau_enabled" + value: true + example: "0 2 * * 0" + + wsfc_cau_max_retries: + type: integer + description: "Maximum CAU retry attempts per node" + required: false + depends_on: + - variable: "wsfc_cau_enabled" + value: true + minimum: 1 + maximum: 10 + default: 3 + + wsfc_cau_max_reboot_time: + type: integer + description: "Maximum reboot time in minutes during CAU" + required: false + depends_on: + - variable: "wsfc_cau_enabled" + value: true + minimum: 5 + maximum: 120 + default: 30 + + # --------------------------------------------------------------------------- + # INTEGRATION & MANAGEMENT + # --------------------------------------------------------------------------- + wsfc_scvmm_managed: + type: boolean + description: "Cluster managed by System Center VMM" + required: false + default: false + + wsfc_scvmm_server: + type: string + description: "SCVMM management server FQDN" + required: false + depends_on: + - variable: "wsfc_scvmm_managed" + value: true + format: fqdn + example: "scvmm.corp.Infinite Improbability Corp.com" + + wsfc_tags: + type: object + description: "Custom tags for cluster resources" + required: false + additionalProperties: + type: string + example: + environment: "production" + owner: "infrastructure-team" + cost_center: "IT-OPS-001" + + # --------------------------------------------------------------------------- + # CLUSTER NETWORK CONFIGURATION + # --------------------------------------------------------------------------- + wsfc_cluster_network_roles: + type: array + description: "Network role assignments for cluster networks" + required: false + items: + type: object + properties: + network_name: + type: string + description: "Name of cluster network" + role: + type: string + description: "Network role" + allowedValues: + - ClusterAndClient + - ClusterOnly + - None + example: + - network_name: "Management" + role: "ClusterAndClient" + - network_name: "Storage" + role: "ClusterOnly" + - network_name: "Backup" + role: "None" + note: "ClusterAndClient = cluster + client traffic, ClusterOnly = cluster heartbeat only, None = excluded from cluster" + +# ============================================================================= +# CLUSTER NODES (GENERIC - SHARED BY AZURE LOCAL & WSFC) +# ============================================================================= +# Generic cluster node configurations used by BOTH Azure Local and Windows +# Server Failover Clustering (WSFC). This is the reusable infrastructure layer +# containing physical hardware specifications, network adapters, and storage +# configurations. +# +# USAGE: +# - Azure Local clusters: Use these nodes with azure_local: configuration +# - WSFC clusters: Use these nodes with wsfc: configuration +# +# This follows the recommended architecture where cluster_nodes: is the shared +# infrastructure layer, while azure_local: and wsfc: provide technology-specific +# cluster configuration. + +cluster_nodes: + infrastructure_type: ["azure_local", "wsfc_s2d", "wsfc_san"] + description: "Array of cluster node configurations (generic, used by both Azure Local and WSFC)" + type: array + required: true + minItems: 1 + maxItems: 16 + items: + type: object + required: [node_hostname, node_management_ip, node_idrac_ip] + properties: + # ----------------------------------------------------------------------- + # HARDWARE IDENTIFICATION + # ----------------------------------------------------------------------- + node_hostname: + type: string + description: "Node hostname" + required: true + maxLength: 15 + example: "azlc-node01" + + node_fqdn: + type: string + description: "Node fully qualified domain name" + required: false + example: "azlc-node01.azlocal.cloud" + + node_model: + type: string + description: "Node server model" + required: false + example: "PowerEdge R750" + + node_serial_number: + type: string + description: "Node server serial number" + required: false + example: "J8KXM73" + + node_service_tag: + type: string + description: "Node Dell service tag" + required: false + example: "J8KXM73" + + node_bios_version: + type: string + description: "Node BIOS firmware version" + required: false + example: "2.4.8" + + # ----------------------------------------------------------------------- + # NETWORK CONFIGURATION + # ----------------------------------------------------------------------- + node_management_ip: + type: string + description: "Node management network IP address" + format: ipv4 + required: true + example: "192.168.211.21" + + node_management_vlan: + type: integer + description: "Node management VLAN ID" + required: false + minimum: 1 + maximum: 4094 + example: 211 + + node_idrac_ip: + type: string + description: "Node iDRAC/BMC IP address" + format: ipv4 + required: true + example: "192.168.215.21" + + node_bmc_ip: + type: string + description: "Node BMC IP address (alternative name for iDRAC IP)" + format: ipv4 + required: false + example: "192.168.215.21" + + node_mac_bmc: + type: string + description: "Node BMC/iDRAC MAC address" + format: mac + required: false + example: "B0:7B:25:D7:8C:40" + + node_mac_port1: + type: string + description: "Node port 1 MAC address" + format: mac + required: false + example: "B8:CE:F6:4C:5D:6E" + + node_mac_port2: + type: string + description: "Node port 2 MAC address" + format: mac + required: false + example: "B8:CE:F6:4C:5D:6F" + + node_mac_port3: + type: string + description: "Node port 3 MAC address" + format: mac + required: false + example: "B8:CE:F6:4C:5D:70" + + node_mac_port4: + type: string + description: "Node port 4 MAC address" + format: mac + required: false + example: "B8:CE:F6:4C:5D:71" + + node_slot3_port1_mac: + type: string + description: "Node slot 3 port 1 MAC address" + format: mac + required: false + example: "B8:CE:F6:4C:5D:72" + + node_slot6_port1_mac: + type: string + description: "Node slot 6 port 1 MAC address" + format: mac + required: false + example: "B8:CE:F6:73:F2:D0" + + node_slot6_port2_mac: + type: string + description: "Node slot 6 port 2 MAC address" + format: mac + required: false + example: "B8:CE:F6:73:F2:D1" + + node_network_ports: + type: integer + description: "Node total number of network ports" + required: false + minimum: 1 + maximum: 16 + example: 4 + + # ----------------------------------------------------------------------- + # CPU SPECIFICATIONS + # ----------------------------------------------------------------------- + node_cpu: + type: string + description: "Node CPU model (short name)" + required: false + example: "Intel Xeon Gold 6248" + + node_cpu_model: + type: string + description: "Node CPU model (detailed string)" + required: false + example: "Intel(R) Xeon(R) Gold 6248 CPU @ 2.50GHz" + + node_cpu_count: + type: integer + description: "Node number of physical CPU sockets" + required: false + minimum: 1 + maximum: 4 + example: 2 + + node_cpu_cores: + type: integer + description: "Node cores per CPU socket" + required: false + minimum: 1 + maximum: 64 + example: 20 + + node_cpu_max_clock_mhz: + type: integer + description: "Node maximum CPU clock speed in MHz" + required: false + minimum: 1000 + maximum: 10000 + example: 3700 + + node_total_cores: + type: integer + description: "Node total physical cores (cpu_count × cpu_cores)" + required: false + minimum: 1 + maximum: 256 + example: 40 + + node_total_logical_procs: + type: integer + description: "Node total logical processors (with hyperthreading)" + required: false + minimum: 1 + maximum: 512 + example: 80 + + # ----------------------------------------------------------------------- + # MEMORY SPECIFICATIONS + # ----------------------------------------------------------------------- + node_memory_type: + type: string + description: "Node memory type (DDR4, DDR5)" + required: false + allowedValues: + - "DDR4" + - "DDR5" + example: "DDR4" + + node_memory_speed_mhz: + type: integer + description: "Node memory speed in MHz" + required: false + minimum: 1600 + maximum: 8000 + example: 2933 + + node_memory_gb: + type: integer + description: "Node memory per DIMM in GB" + required: false + minimum: 4 + maximum: 256 + example: 32 + + node_total_memory_gb: + type: integer + description: "Node total system memory in GB" + required: false + minimum: 32 + maximum: 6144 + example: 512 + + # ----------------------------------------------------------------------- + # STORAGE SPECIFICATIONS + # ----------------------------------------------------------------------- + node_boot_drive_size_gb: + type: integer + description: "Node boot drive size in GB (typically SSD/NVMe)" + required: false + minimum: 100 + maximum: 2000 + example: 480 + + node_data_drive_count: + type: integer + description: "Node number of data drives (for Storage Spaces Direct)" + required: false + minimum: 0 + maximum: 24 + example: 8 + + node_data_drive_size_gb: + type: integer + description: "Node size of each data drive in GB" + required: false + minimum: 100 + maximum: 10000 + example: 1920 + + # ----------------------------------------------------------------------- + # PHYSICAL LOCATION & CABLING + # ----------------------------------------------------------------------- + node_rack_position: + type: string + description: "Node physical rack location (rack ID and U position)" + required: false + example: "RAL-Rack01-U10" + + node_cabling_idrac: + type: string + description: "Node iDRAC cabling documentation" + required: false + example: "Port 1 to Switch A Port 1" + + # Hardware Platform Configuration + hardware_platform: + type: object + description: "Node hardware configuration (platform model, adapters, GPUs)" + required: false + properties: + platform_model: + type: string + description: "Dell AX platform model" + required: true + allowedValues: + - "AX-4510c" + - "AX-660" + - "AX-760" + example: "AX-760" + + # Onboard NICs (AX-660/760 only, typically disabled) + onboard_nics: + type: object + description: "Onboard 1GbE/10GbE NICs (AX-660/760 only)" + required: false + properties: + enabled: + type: boolean + description: "Whether onboard NICs are enabled (typically disabled)" + default: false + adapters: + type: array + description: "Onboard NIC adapter configurations" + required: false + items: + type: object + properties: + name: + type: string + description: "Adapter name as seen by Windows/ARM template" + example: "Embedded LOM 1" + mac_address: + type: string + format: mac + description: "Adapter MAC address" + example: "AA:BB:CC:DD:EE:01" + speed_gbps: + type: integer + description: "Port speed in Gbps" + allowedValues: [1, 10] + example: 10 + + # PCIe Network Adapters (main data path) + pcie_network_adapters: + type: array + description: "PCIe network adapter cards installed in node" + required: false + items: + type: object + properties: + slot: + type: integer + description: "PCIe slot number" + minimum: 1 + maximum: 8 + example: 2 + vendor: + type: string + description: "NIC card vendor" + allowedValues: + - "Mellanox" + - "NVIDIA" + - "Intel" + - "Broadcom" + example: "Mellanox" + model: + type: string + description: "NIC card model" + example: "ConnectX-6 Dx" + ports: + type: array + description: "Physical ports on this NIC card" + minItems: 1 + maxItems: 4 + items: + type: object + properties: + name: + type: string + description: "Port name as seen by Windows/ARM template (Mellanox: 'Slot X Port Y', Intel: 'Embedded NIC X')" + example: "Slot 2 Port 1" + mac_address: + type: string + format: mac + description: "Port MAC address" + example: "B8:CE:F6:4C:5D:6E" + speed_gbps: + type: integer + description: "Port speed in Gbps" + allowedValues: [10, 25, 40, 50, 100, 200] + example: 25 + rdma_capable: + type: boolean + description: "Whether port supports RDMA" + default: true + rdma_technology: + type: string + description: "RDMA technology (if rdma_capable)" + allowedValues: + - "RoCEv2" + - "iWARP" + - "InfiniBand" + example: "RoCEv2" + + # PCIe GPU Accelerators (optional) + pcie_gpus: + type: array + description: "PCIe GPU accelerator cards installed in node" + required: false + items: + type: object + properties: + slot: + type: integer + description: "PCIe slot number" + minimum: 1 + maximum: 8 + example: 5 + vendor: + type: string + description: "GPU vendor" + allowedValues: + - "NVIDIA" + - "AMD" + example: "NVIDIA" + model: + type: string + description: "GPU model" + example: "A40" + memory_gb: + type: integer + description: "GPU memory in GB" + example: 48 + partition_capable: + type: boolean + description: "Whether GPU supports partitioning (vGPU or MIG)" + default: false + partition_type: + type: string + description: "GPU partition technology" + allowedValues: + - "vGPU" + - "MIG" + - "none" + example: "vGPU" + vgpu_profiles: + type: array + description: "Available vGPU profiles (if partition_type is vGPU)" + required: false + items: + type: string + example: ["A40-1Q", "A40-2Q", "A40-4Q", "A40-8Q", "A40-12Q", "A40-24Q"] + mig_profiles: + type: array + description: "Available MIG profiles (if partition_type is MIG)" + required: false + items: + type: string + example: ["1g.10gb", "2g.20gb", "3g.40gb"] + +# ============================================================================= +# CLUSTER ARM DEPLOYMENT +# ============================================================================= + +cluster_arm_deployment: + infrastructure_type: ["azure_local"] + arm_key_vault_name: + type: string + description: "Key Vault name for ARM deployment" + required: false + minLength: 3 + maxLength: 24 + pattern: "^[a-z][a-z0-9]+(-[a-z0-9]+)*$" + note: "No consecutive hyphens allowed" + example: "kv-azlc-mgmt" + + arm_logs_retention_days: + type: integer + description: "Logs retention days" + required: false + minimum: 30 + maximum: 730 + default: 90 + + arm_cluster_name: + type: string + description: "Cluster name for ARM deployment" + required: false + maxLength: 15 + pattern: "^[a-zA-Z][a-zA-Z0-9-]*$" + example: "azlc-clus01" + + arm_witness_type: + type: string + description: "Witness type" + required: false + allowedValues: + - cloud_witness + - file_share_witness + - disk_witness + default: "cloud_witness" + + arm_domain_fqdn: + type: string + description: "Domain FQDN" + required: false + example: "azlocal.cloud" + + arm_ou_path: + type: string + description: "OU path" + required: false + example: "OU=AzureLocal,DC=azlocal,DC=cloud" + + arm_naming_prefix: + type: string + description: "Naming prefix" + required: false + maxLength: 8 + pattern: "^[a-z0-9-]+$" + example: "azlc" + + arm_subnet_mask: + type: string + description: "Subnet mask" + required: false + format: ipv4 + example: "255.255.255.0" + + arm_default_gateway: + type: string + description: "Default gateway" + required: false + format: ipv4 + example: "192.168.211.1" + + arm_starting_ip: + type: string + description: "Starting IP address" + required: false + format: ipv4 + example: "192.168.211.20" + + arm_ending_ip: + type: string + description: "Ending IP address" + required: false + format: ipv4 + example: "192.168.211.25" + + arm_service_principal_app_id: + category: "identity" + subcategory: "service-principal" + type: string + description: "ARM deployment service principal app ID (Azure Local cluster deployments)" + required: false + format: uuid + example: "00000000-aaaa-bbbb-cccc-333333333333" + + arm_service_principal_object_id: + category: "identity" + subcategory: "service-principal" + type: string + description: "ARM deployment service principal object ID (Azure Local cluster deployments)" + required: false + format: uuid + example: "12345678-1234-1234-1234-123456789012" + + arm_service_principal_secret_ref: + category: "identity" + subcategory: "service-principal" + type: string + description: "ARM deployment service principal secret reference (Azure Local cluster deployments)" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-mgmt/spn-client-secret" + + arm_resource_provider_object_id: + type: string + description: "Resource provider object ID" + required: false + format: uuid + example: "12345678-1234-1234-1234-123456789012" + + arm_mode: + type: string + description: "ARM template deployment mode (validate_only: validate config without deploying, deploy: actual deployment)" + required: false + allowedValues: + - validate_only + - deploy + default: "deploy" + + arm_storage_configuration_mode: + type: string + description: "Storage configuration mode in ARM template (express: automated storage config, advanced: manual storage config)" + required: false + allowedValues: + - express + - advanced + default: "express" + + # --------------------------------------------------------------------------- + # AZURE ARC INTEGRATION + # --------------------------------------------------------------------------- + arc_node_resource_ids: + type: array + description: "Azure Arc resource IDs for cluster nodes (Arc machines that are part of the cluster) - ARM template parameter" + required: false + items: + type: string + format: azure_resource_id + minItems: 1 + maxItems: 16 + example: + - "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-landingzone-cluster/providers/Microsoft.HybridCompute/machines/azlc-node01" + - "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-landingzone-cluster/providers/Microsoft.HybridCompute/machines/azlc-node02" + + arm_cluster_telemetry_setting: + type: string + description: "Telemetry collection level for ARM deployment (full: all telemetry, basic: essential only, minimal: errors only)" + required: false + allowedValues: + - full + - basic + - minimal + default: "full" + + arm_cluster_telemetry_eu_location: + type: boolean + description: "Use EU data residency for telemetry in ARM deployment" + required: false + default: false + + arm_diagnostics_logs_retention_days: + type: integer + description: "Diagnostic logs retention period for ARM deployment (days)" + required: false + minimum: 30 + maximum: 730 + default: 90 + + # Security Settings (ARM Template Deployment Parameters) + arm_security_level: + type: string + description: "Security configuration profile applied during ARM deployment" + required: false + allowedValues: + - recommended + - customized + default: "recommended" + + arm_credential_guard_enforced: + type: boolean + description: "Credential Guard enforced during ARM deployment" + required: false + default: true + + arm_smb_signing_enforced: + type: boolean + description: "SMB signing enforced during ARM deployment" + required: false + default: true + + arm_smb_cluster_encryption: + type: boolean + description: "SMB cluster encryption during ARM deployment" + required: false + default: false + + arm_bitlocker_boot_volume: + type: boolean + description: "BitLocker boot volume encryption during ARM deployment" + required: false + default: true + + arm_bitlocker_data_volumes: + type: boolean + description: "BitLocker data volumes encryption during ARM deployment" + required: false + default: true + + arm_wdac_enforced: + type: boolean + description: "WDAC (Application Control) enforced during ARM deployment" + required: false + default: true + + arm_eu_location: + type: boolean + description: "EU location for telemetry data during ARM deployment" + required: false + default: false + + # --------------------------------------------------------------------------- + # CLUSTER TOPOLOGY & RACK AWARENESS + # --------------------------------------------------------------------------- + arm_cluster_pattern: + type: string + description: "Cluster deployment pattern (Standard: single rack, RackAware: multi-rack with zone awareness)" + required: false + allowedValues: + - Standard + - RackAware + default: "Standard" + references: + - url: "https://learn.microsoft.com/azure/azure-local/deploy/rack-aware-cluster-deployment-via-template" + + arm_local_availability_zones: + type: array + description: "Local availability zones for rack-aware deployments - maps nodes to physical racks" + required: false + items: + type: object + properties: + localAvailabilityZoneName: + type: string + description: "Zone name (e.g., Zone1, Zone2)" + nodes: + type: array + description: "Node hostnames in this zone" + items: + type: string + example: + - localAvailabilityZoneName: "Zone1" + nodes: ["node1", "node2"] + - localAvailabilityZoneName: "Zone2" + nodes: ["node3", "node4"] + + # --------------------------------------------------------------------------- + # NETWORK CONFIGURATION (EXTENDED) + # --------------------------------------------------------------------------- + arm_dns_servers: + type: array + description: "List of DNS server IP addresses" + required: false + items: + type: string + format: ipv4 + example: + - "192.168.211.10" + - "192.168.211.11" + + arm_use_dhcp: + type: boolean + description: "Use DHCP for hosts and cluster IPs (if true, gateway and DNS not required)" + required: false + default: false + + arm_physical_nodes_settings: + type: array + description: "Array of physical nodes with their IP addresses" + required: false + items: + type: object + properties: + name: + type: string + description: "Node hostname" + ipv4Address: + type: string + format: ipv4 + description: "Node IPv4 address" + example: + - name: "azlc-node01" + ipv4Address: "192.168.211.21" + - name: "azlc-node02" + ipv4Address: "192.168.211.22" + + arm_networking_type: + type: string + description: "Type of networking deployment" + required: false + allowedValues: + - switchedMultiServerDeployment + - switchlessMultiServerDeployment + example: "switchedMultiServerDeployment" + + arm_networking_pattern: + type: string + description: "Network deployment pattern" + required: false + allowedValues: + - hyperConverged + - disaggregated + example: "hyperConverged" + + arm_intent_list: + type: array + description: "List of deployment network intents" + required: false + items: + type: object + properties: + name: + type: string + trafficType: + type: array + items: + type: string + adapter: + type: array + items: + type: string + overrideVirtualSwitchConfiguration: + type: boolean + virtualSwitchConfigurationOverrides: + type: object + + # --------------------------------------------------------------------------- + # STORAGE CONFIGURATION (EXTENDED) + # --------------------------------------------------------------------------- + arm_storage_network_list: + type: array + description: "List of storage networks for cluster" + required: false + items: + type: object + properties: + name: + type: string + networkAdapterName: + type: string + vlanId: + type: integer + + arm_storage_connectivity_switchless: + type: boolean + description: "Storage connectivity configured without network switches" + required: false + default: false + + arm_enable_storage_auto_ip: + type: boolean + description: "Enable automatic IP assignment for storage network" + required: false + default: false + + # --------------------------------------------------------------------------- + # CREDENTIALS & ADMIN ACCOUNTS + # --------------------------------------------------------------------------- + arm_local_admin_password: + type: string + description: "Password for local administrator on all cluster machines" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-mgmt/local-admin-password" + + arm_lcm_admin_username: + type: string + description: "Username for LCM (Lifecycle Manager) admin" + required: false + example: "azurestackadmin" + + arm_lcm_admin_password: + type: string + description: "Password for LCM admin" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-mgmt/lcm-deployment-password" + + # --------------------------------------------------------------------------- + # OBSERVABILITY & DIAGNOSTICS (EXTENDED) + # --------------------------------------------------------------------------- + arm_episodic_data_upload: + type: boolean + description: "Collect log data and upload to Microsoft for troubleshooting (crash dumps)" + required: false + default: true + + arm_streaming_data_client: + type: boolean + description: "Enable telemetry data streaming from cluster to Microsoft" + required: false + default: true + + arm_drift_control_enforced: + type: boolean + description: "Reapply security defaults regularly to prevent drift" + required: false + default: true + + # --------------------------------------------------------------------------- + # SOLUTION BUILDER EXTENSION (SBE) - OEM/PARTNER + # --------------------------------------------------------------------------- + arm_sbe_version: + type: string + description: "Version of Solution Builder Extension (SBE) for deployment" + required: false + example: "1.0.0" + + arm_sbe_family: + type: string + description: "Family or category of SBE package" + required: false + example: "Dell" + + arm_sbe_publisher: + type: string + description: "Publisher or vendor of SBE" + required: false + example: "Dell" + + arm_sbe_manifest_source: + type: string + description: "Source location of SBE manifest file" + required: false + format: url + + arm_sbe_manifest_creation_date: + type: string + description: "Creation date of SBE manifest" + required: false + format: date + + arm_partner_properties: + type: array + description: "List of partner-specific properties" + required: false + items: + type: object + + arm_partner_credential_list: + type: array + description: "List of partner credentials" + required: false + items: + type: object + + # --------------------------------------------------------------------------- + # ADDITIONAL CONFIGURATION + # --------------------------------------------------------------------------- + arm_custom_location: + type: string + description: "Custom location for deployment (Arc custom location resource)" + required: false + format: azure_resource_id + + arm_storage_account_type: + type: string + description: "Type of Azure Storage Account for diagnostics" + required: false + allowedValues: + - Standard_LRS + - Standard_GRS + - Standard_RAGRS + default: "Standard_LRS" + default: "Standard_LRS" + + arm_diagnostic_storage_account_name: + type: string + description: "Name of Azure Storage Account for Key Vault audit logs" + required: false + minLength: 3 + maxLength: 24 + pattern: "^[a-z0-9]+$" + example: "stazlciiceus001" + + arm_soft_delete_retention_days: + type: integer + description: "Key Vault soft delete retention days (7-90)" + required: false + minimum: 7 + maximum: 90 + default: 90 + + +# ============================================================================= +# NETWORK INTENTS +# ============================================================================= +# Network intents define which physical network adapters are used for different traffic types. +# This section supports 1-16 intents for maximum flexibility: +# - 1 intent: Fully converged (all traffic on same adapters) +# - 2 intents: Management+Compute / Storage (most common) +# - 3+ intents: Fully disaggregated or custom configurations +# +# RELATED SECTIONS: +# - logical_networks: (line 5658+) - VM networking layer built on top of intents +# - cluster_nodes: (line 4732+) - Physical adapters referenced by intents +# - vlans: (line 3264+) - VLAN IDs used by logical networks +# +# Each intent is self-contained with all its properties in one place. +# All parameters are validated against Microsoft ARM template requirements. + +network_intents: + infrastructure_type: ["azure_local"] + type: array + description: "Network intent configurations (1-16 intents supported). Each intent defines traffic types, adapters, and network settings." + required: true + minItems: 1 + maxItems: 16 + items: + type: object + required: + - name + - traffic_types + - adapter_names + properties: + # Intent Identification + name: + type: string + description: "Intent name (e.g., 'MgmtCompute', 'Storage', 'Converged')" + required: true + example: "MgmtCompute" + + # Traffic Types (Microsoft ARM parameter: intentList[].trafficType) + traffic_types: + type: array + description: "Traffic types for this intent (ARM parameter: trafficType)" + required: true + items: + type: string + enum: ["Management", "Compute", "Storage"] + minItems: 1 + maxItems: 3 + example: ["Management", "Compute"] + + # Physical Adapters (Microsoft ARM parameter: intentList[].adapter) + adapter_names: + type: array + description: "Physical network adapter names (minimum 2 for HA, ARM parameter: adapter)" + required: true + items: + type: string + minItems: 2 + example: ["Slot 2 Port 1", "Slot 2 Port 2"] + + # Management/Compute Network Settings + subnet: + type: string + description: "Network subnet (CIDR) for management/compute traffic" + required: false + format: cidr + example: "192.168.211.0/24" + + gateway: + type: string + description: "Network gateway for management/compute traffic" + required: false + format: ipv4 + example: "192.168.211.1" + + vlan_id: + type: integer + description: "VLAN ID for management/compute traffic" + required: false + minimum: 0 + maximum: 4094 + example: 211 + + dns_servers: + type: array + description: "DNS servers for management traffic" + required: false + items: + type: string + format: ipv4 + example: + - "192.168.211.10" + - "192.168.211.11" + + # Storage Network Settings (Microsoft ARM parameter: storageNetworkList) + storage_networks: + type: array + description: "Storage network configurations (ARM parameter: storageNetworkList)" + required: false + items: + type: object + properties: + subnet: + type: string + description: "Storage network subnet (CIDR)" + format: cidr + example: "10.71.212.0/24" + vlan_id: + type: integer + description: "Storage VLAN ID" + minimum: 1 + maximum: 4094 + example: 712 + minItems: 1 + maxItems: 4 + example: + - subnet: "10.71.212.0/24" + vlan_id: 712 + - subnet: "10.71.213.0/24" + vlan_id: 713 + + # Storage Configuration (Microsoft ARM parameters) + enable_storage_auto_ip: + type: boolean + description: "Enable automatic IP assignment for storage (ARM parameter: enableStorageAutoIp). Must be FALSE for switchless storage." + required: false + default: true + + storage_connectivity_switchless: + type: boolean + description: "Use switchless storage topology (ARM parameter: storageConnectivitySwitchless). Max 4 nodes, requires manual IPs." + required: false + default: false + + # Storage Adapter IPs (for switchless or manual IP assignment) + storage_adapter_ips: + type: array + description: "Manual IP assignment for storage adapters (ARM parameter: storageAdapterIPInfo). Required when enable_storage_auto_ip is false." + required: false + items: + type: object + properties: + node_name: + type: string + description: "Cluster node name" + example: "azlc-node01" + adapter_name: + type: string + description: "Physical adapter name" + example: "Slot 6 Port 1" + ipv4_address: + type: string + description: "IPv4 address for this adapter" + format: ipv4 + example: "10.71.212.1" + subnet_mask: + type: string + description: "Subnet mask" + format: ipv4 + example: "255.255.255.0" + example: + - node_name: "azlc-node01" + adapter_name: "Slot 6 Port 1" + ipv4_address: "10.71.212.1" + subnet_mask: "255.255.255.0" + + # Adapter Property Overrides (Microsoft ARM parameter: adapterPropertyOverrides) + jumbo_packet_size: + type: integer + description: "Jumbo packet (MTU) size (ARM parameter: adapterPropertyOverrides.jumboPacket)" + required: false + minimum: 1500 + maximum: 9014 + default: 9014 + example: 9014 + + network_direct_enabled: + type: boolean + description: "Enable RDMA/NetworkDirect (ARM parameter: adapterPropertyOverrides.networkDirect)" + required: false + default: true + + network_direct_technology: + type: string + description: "RDMA technology (ARM parameter: adapterPropertyOverrides.networkDirectTechnology)" + required: false + allowedValues: + - iWARP + - RoCEv2 + default: "RoCEv2" + example: "RoCEv2" + + # Example configurations (for reference - not part of the schema) + # Three complexity levels: minimal, standard, complete + examples: + minimal: + # Simplest valid configuration - single converged intent + # Use case: Testing, POC, or simple lab environments + - name: "Converged" + traffic_types: ["Management", "Compute", "Storage"] + adapter_names: ["Slot 2 Port 1", "Slot 2 Port 2"] + subnet: "192.168.211.0/24" + gateway: "192.168.211.1" + vlan_id: 211 + dns_servers: ["192.168.211.10"] + storage_networks: + - subnet: "10.71.212.0/24" + vlan_id: 712 + enable_storage_auto_ip: true + + standard: + # Production-ready 2-intent deployment (most common) + # Use case: Standard production deployments with separated storage + - name: "MgmtCompute" + traffic_types: ["Management", "Compute"] + adapter_names: ["Slot 2 Port 1", "Slot 2 Port 2"] + subnet: "192.168.211.0/24" + gateway: "192.168.211.1" + vlan_id: 211 + dns_servers: ["192.168.211.10", "192.168.211.11"] + + - name: "Storage" + traffic_types: ["Storage"] + adapter_names: ["Slot 6 Port 1", "Slot 6 Port 2"] + storage_networks: + - subnet: "10.71.212.0/24" + vlan_id: 712 + - subnet: "10.71.213.0/24" + vlan_id: 713 + enable_storage_auto_ip: true + jumbo_packet_size: 9014 + network_direct_enabled: true + network_direct_technology: "RoCEv2" + + complete: + # Advanced configuration with all options (switchless storage) + # Use case: High-performance clusters with manual IP control (1-4 nodes max) + - name: "MgmtCompute" + traffic_types: ["Management", "Compute"] + adapter_names: ["Slot 2 Port 1", "Slot 2 Port 2"] + subnet: "192.168.211.0/24" + gateway: "192.168.211.1" + vlan_id: 211 + dns_servers: ["192.168.211.10", "192.168.211.11"] + jumbo_packet_size: 9014 + + - name: "Storage" + traffic_types: ["Storage"] + adapter_names: ["Slot 6 Port 1", "Slot 6 Port 2", "Slot 6 Port 3", "Slot 6 Port 4"] + storage_networks: + - subnet: "10.71.212.0/24" + vlan_id: 712 + - subnet: "10.71.213.0/24" + vlan_id: 713 + enable_storage_auto_ip: false # Required for switchless + storage_connectivity_switchless: true + jumbo_packet_size: 9014 + network_direct_enabled: true + network_direct_technology: "RoCEv2" + storage_adapter_ips: + - node_name: "azlc-node01" + adapter_name: "Slot 6 Port 1" + ipv4_address: "10.71.212.1" + subnet_mask: "255.255.255.0" + - node_name: "azlc-node01" + adapter_name: "Slot 6 Port 2" + ipv4_address: "10.71.212.2" + subnet_mask: "255.255.255.0" + - node_name: "azlc-node02" + adapter_name: "Slot 6 Port 1" + ipv4_address: "10.71.212.3" + subnet_mask: "255.255.255.0" + - node_name: "azlc-node02" + adapter_name: "Slot 6 Port 2" + ipv4_address: "10.71.212.4" + subnet_mask: "255.255.255.0" + +# ============================================================================= +# LOGICAL NETWORKS +# ============================================================================= +# Logical network configurations for VM networking layer (1-16 networks supported) +# Management-Compute network is primary and required (VLAN 211) +# Additional workload networks are optional and scalable +# +# RELATED SECTIONS: +# - network_intents: (line 5391+) - Physical adapter layer (intents must exist first) +# - vlans: (line 3264+) - VLAN definitions and IP addressing +# - dhcp: (line 3675+) - DHCP scope configuration for each network +# +# ARM TEMPLATE MAPPING: +# logicalNetworks: [ +# { +# name: string +# addressPrefix: string (CIDR) +# defaultGateway: string (IPv4) +# vlan: integer (0-4094) +# dnsServers: array +# ipAllocationMethod: "Static" | "Dynamic" +# ipPools: [ { name, start, end, type } ] +# dhcpOptions: { dnsServers, domainName, ntpServers } +# routes: [ { name, addressPrefix, nextHop } ] +# } +# ] +# +# EXAMPLES: +# 1. Management-Compute network (PRIMARY - always required): +# - name: "ln-management-compute" +# address_prefix: "192.168.211.0/24" +# vlan_id: 211 +# ip_pools: [{ start: "192.168.211.100", end: "192.168.211.200" }] +# +# 2. Multiple networks (management + workloads): +# - name: "ln-management-compute" +# vlan_id: 211 +# - name: "ln-workload-web" +# vlan_id: 501 +# - name: "ln-workload-app" +# vlan_id: 502 +# +# 3. Advanced with DHCP options and routes: +# - name: "ln-management-compute" +# dhcp_options: +# dns_servers: ["192.168.211.10"] +# domain_name: "azlocal.cloud" +# routes: +# - name: "default-route" +# address_prefix: "0.0.0.0/0" +# next_hop: "192.168.211.1" + +logical_networks: + infrastructure_type: ["azure_local"] + type: array + description: "Logical network configurations (VM networking layer). Management-Compute network is primary and required." + required: true + minItems: 1 + maxItems: 16 + items: + type: object + required: + - name + - address_prefix + - default_gateway + - vlan_id + properties: + # Network Identification + name: + type: string + description: "Logical network name (ARM parameter: name)" + required: true + example: "ln-management-compute" + + display_name: + type: string + description: "Display name for Azure portal" + required: false + example: "Management-Compute Network" + + # Network Configuration (ARM parameters) + address_prefix: + type: string + description: "Network address prefix in CIDR notation (ARM parameter: addressPrefix)" + required: true + format: cidr + example: "192.168.211.0/24" + + default_gateway: + type: string + description: "Default gateway for the network (ARM parameter: defaultGateway)" + required: true + format: ipv4 + example: "192.168.211.1" + + vlan_id: + type: integer + description: "VLAN ID for network isolation (ARM parameter: vlan)" + required: true + minimum: 0 + maximum: 4094 + example: 211 + + dns_servers: + type: array + description: "DNS servers for the network (ARM parameter: dnsServers)" + required: false + items: + type: string + format: ipv4 + example: + - "192.168.211.10" + - "192.168.211.11" + + # IP Allocation (ARM parameter: ipAllocationMethod and ipPools) + ip_allocation_method: + type: string + description: "IP allocation method (ARM parameter: ipAllocationMethod)" + required: false + allowedValues: + - Static + - Dynamic + default: "Static" + example: "Static" + + ip_pools: + type: array + description: "IP address pools for VM allocation (ARM parameter: ipPools). Supports multiple pools per network." + required: false + items: + type: object + required: + - start + - end + properties: + name: + type: string + description: "IP pool name" + required: false + example: "default-pool" + start: + type: string + description: "Starting IP address" + required: true + format: ipv4 + example: "192.168.211.100" + end: + type: string + description: "Ending IP address" + required: true + format: ipv4 + example: "192.168.211.200" + type: + type: string + description: "Pool type (vm, k8s, etc.)" + required: false + default: "vm" + example: "vm" + minItems: 1 + example: + - name: "vm-pool" + start: "192.168.211.100" + end: "192.168.211.200" + type: "vm" + + # DHCP Options (ARM parameter: dhcpOptions) + dhcp_options: + type: object + description: "DHCP configuration options (ARM parameter: dhcpOptions)" + required: false + properties: + dns_servers: + type: array + description: "DHCP DNS servers" + required: false + items: + type: string + format: ipv4 + example: + - "192.168.211.10" + domain_name: + type: string + description: "DHCP domain name" + required: false + example: "azlocal.cloud" + ntp_servers: + type: array + description: "NTP servers for time sync" + required: false + items: + type: string + example: + - "192.168.211.1" + example: + dns_servers: ["192.168.211.10"] + domain_name: "azlocal.cloud" + + # Routes (ARM parameter: routes) + routes: + type: array + description: "Static routes for the network (ARM parameter: routes)" + required: false + items: + type: object + required: + - address_prefix + - next_hop + properties: + name: + type: string + description: "Route name" + required: false + example: "default-route" + address_prefix: + type: string + description: "Destination address prefix" + required: true + format: cidr + example: "0.0.0.0/0" + next_hop: + type: string + description: "Next hop IP address" + required: true + format: ipv4 + example: "192.168.211.1" + example: + - name: "default-route" + address_prefix: "0.0.0.0/0" + next_hop: "192.168.211.1" + + # Additional Settings + immutable: + type: boolean + description: "Network configuration is immutable after creation" + required: false + default: false + + network_intent_binding: + type: string + description: "Bind to specific network intent (optional)" + required: false + example: "MgmtCompute" + + # Example configurations (for reference - not part of the schema) + # Three complexity levels: minimal, standard, complete + examples: + minimal: + # Simplest valid configuration - single management-compute network + # Use case: Testing, POC, or simple lab environments + - name: "ln-management-compute" + address_prefix: "192.168.211.0/24" + default_gateway: "192.168.211.1" + vlan_id: 211 + dns_servers: ["192.168.211.10"] + ip_allocation_method: "Static" + ip_pools: + - start: "192.168.211.100" + end: "192.168.211.200" + + standard: + # Production multi-network deployment (management + workloads) + # Use case: Standard production with separated workload networks + + # Network 1: Management-Compute (PRIMARY - always required) + - name: "ln-management-compute" + display_name: "Management-Compute Network" + address_prefix: "192.168.211.0/24" + default_gateway: "192.168.211.1" + vlan_id: 211 + dns_servers: ["192.168.211.10", "192.168.211.11"] + ip_allocation_method: "Static" + ip_pools: + - name: "management-vm-pool" + start: "192.168.211.100" + end: "192.168.211.150" + type: "vm" + - name: "arc-vm-pool" + start: "192.168.211.151" + end: "192.168.211.200" + type: "vm" + dhcp_options: + dns_servers: ["192.168.211.10", "192.168.211.11"] + domain_name: "azlocal.cloud" + network_intent_binding: "MgmtCompute" + + # Network 2: Workload-Web + - name: "ln-workload-web" + display_name: "Web Tier Network" + address_prefix: "192.168.100.0/24" + default_gateway: "192.168.100.1" + vlan_id: 501 + dns_servers: ["192.168.211.10"] + ip_allocation_method: "Static" + ip_pools: + - name: "web-vm-pool" + start: "192.168.100.100" + end: "192.168.100.200" + type: "vm" + network_intent_binding: "MgmtCompute" + + # Network 3: Workload-App + - name: "ln-workload-app" + display_name: "Application Tier Network" + address_prefix: "192.168.51.0/24" + default_gateway: "192.168.51.1" + vlan_id: 502 + dns_servers: ["192.168.211.10"] + ip_allocation_method: "Static" + ip_pools: + - name: "app-vm-pool" + start: "192.168.51.100" + end: "192.168.51.200" + type: "vm" + network_intent_binding: "MgmtCompute" + + complete: + # Advanced configuration with all optional features + # Use case: Enterprise production with routing, DHCP options, multiple IP pools + - name: "ln-management-compute" + display_name: "Management-Compute Network" + address_prefix: "192.168.211.0/24" + default_gateway: "192.168.211.1" + vlan_id: 211 + dns_servers: + - "192.168.211.10" + - "192.168.211.11" + ip_allocation_method: "Static" + ip_pools: + - name: "management-pool" + start: "192.168.211.100" + end: "192.168.211.130" + type: "vm" + - name: "arc-vm-pool" + start: "192.168.211.131" + end: "192.168.211.160" + type: "vm" + - name: "kubernetes-pool" + start: "192.168.211.161" + end: "192.168.211.200" + type: "k8s" + dhcp_options: + dns_servers: + - "192.168.211.10" + - "192.168.211.11" + domain_name: "azlocal.cloud" + ntp_servers: + - "192.168.211.1" + routes: + - name: "default-route" + address_prefix: "0.0.0.0/0" + next_hop: "192.168.211.1" + - name: "azure-wire-server" + address_prefix: "168.63.129.16/32" + next_hop: "192.168.211.1" + - name: "corp-network" + address_prefix: "10.0.0.0/8" + next_hop: "192.168.211.254" + network_intent_binding: "MgmtCompute" + immutable: false + +# ============================================================================= +# NETWORK DEVICES +# ============================================================================= +# Network infrastructure device configurations - generic array structure supporting +# any combination of switches, firewalls, routers, console servers, etc. +# Fully optional and scalable (0-32+ devices per environment). + +network_devices: + infrastructure_type: ["wsfc_s2d", "wsfc_san"] + type: array + description: "Network infrastructure device configurations (switches, firewalls, routers, console servers). Optional and scalable." + required: false + minItems: 0 + maxItems: 32 + items: + type: object + required: [device_type, device_role, device_hostname, device_management_ip] + properties: + # ----------------------------------------------------------------------- + # DEVICE IDENTIFICATION + # ----------------------------------------------------------------------- + device_type: + type: string + description: "Device type classification" + required: true + allowedValues: + - switch + - firewall + - router + - console_server + - wireless_controller + - load_balancer + example: "switch" + + device_role: + type: string + description: "Device role in infrastructure" + required: true + allowedValues: + - primary + - secondary + - standalone + example: "primary" + + device_hostname: + type: string + description: "Device hostname" + required: true + maxLength: 15 + example: "switch01" + + device_management_ip: + type: string + description: "Device management IP address" + format: ipv4 + required: true + example: "192.168.211.10" + + device_management_vlan: + type: integer + description: "Device management VLAN ID" + required: false + minimum: 0 + maximum: 4094 + example: 211 + + # ----------------------------------------------------------------------- + # HARDWARE DETAILS + # ----------------------------------------------------------------------- + device_vendor: + type: string + description: "Device manufacturer/vendor" + required: false + allowedValues: + - Dell + - Fortinet + - Cisco + - Arista + - Ubiquiti + - OpenGear + - HPE + - Juniper + example: "Dell" + + device_model: + type: string + description: "Device model number" + required: false + example: "S5248F-ON" + + device_serial_number: + type: string + description: "Device serial number" + required: false + example: "CN7942V1AB" + + device_firmware_version: + type: string + description: "Device firmware/OS version" + required: false + example: "10.5.5.5" + + device_mac_address: + type: string + description: "Device MAC address" + format: mac + required: false + example: "AA:BB:CC:DD:EE:FF" + + # ----------------------------------------------------------------------- + # CREDENTIALS (Key Vault References) + # ----------------------------------------------------------------------- + device_username_secret: + type: string + description: "Device username (Key Vault secret reference)" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-mgmt/switch-username" + + device_password_secret: + type: string + description: "Device password (Key Vault secret reference)" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-mgmt/switch-password" + + device_management_protocol: + type: string + description: "Device management protocol" + required: false + allowedValues: + - SSH + - HTTPS + - Telnet + - SNMP + default: "SSH" + example: "SSH" + + # ----------------------------------------------------------------------- + # HIGH AVAILABILITY CONFIGURATION + # ----------------------------------------------------------------------- + device_ha_enabled: + type: boolean + description: "High availability enabled" + required: false + default: false + example: true + + device_ha_mode: + type: string + description: "High availability mode" + required: false + allowedValues: + - active-active + - active-passive + - stack + example: "active-active" + + device_ha_priority: + type: integer + description: "HA priority (higher value = higher priority)" + required: false + minimum: 0 + maximum: 255 + example: 200 + + device_ha_sync_interface: + type: string + description: "HA heartbeat/sync interface name" + required: false + example: "port5" + + device_ha_virtual_ip: + type: string + description: "HA virtual/cluster IP address" + format: ipv4 + required: false + example: "192.168.211.254" + + # ----------------------------------------------------------------------- + # CONSOLE SERVER SPECIFIC + # ----------------------------------------------------------------------- + device_serial_ports: + type: integer + description: "Number of serial console ports" + required: false + minimum: 0 + maximum: 96 + example: 48 + + device_network_ports: + type: integer + description: "Number of network switch ports" + required: false + minimum: 0 + maximum: 48 + example: 4 + + # ----------------------------------------------------------------------- + # ROUTING & NETWORKING + # ----------------------------------------------------------------------- + device_wan_ip: + type: string + description: "WAN/external IP address" + format: ipv4 + required: false + example: "203.0.113.1" + + device_gateway_ips: + type: array + description: "Gateway IP addresses managed by device" + required: false + items: + type: string + format: ipv4 + example: + - "192.168.211.1" + + device_static_routes: + type: array + description: "Static routes configured on device" + required: false + items: + type: object + properties: + destination: + type: string + description: "Destination network (CIDR)" + example: "10.0.0.0/8" + gateway: + type: string + description: "Next-hop gateway IP" + format: ipv4 + example: "192.168.211.1" + + # ----------------------------------------------------------------------- + # SWITCH SPECIFIC + # ----------------------------------------------------------------------- + device_port_config: + type: array + description: "Port configuration for switches" + required: false + items: + type: object + properties: + port: + type: string + description: "Port identifier" + example: "1/1" + vlan: + type: integer + description: "VLAN ID" + minimum: 0 + maximum: 4094 + example: 211 + mode: + type: string + description: "Port mode" + allowedValues: [access, trunk] + example: "access" + + # ----------------------------------------------------------------------- + # FIREWALL SPECIFIC + # ----------------------------------------------------------------------- + device_policy_name: + type: string + description: "Firewall policy/ruleset name" + required: false + example: "fp-azlc-iic" + + # ----------------------------------------------------------------------- + # SUPPORT & DOCUMENTATION + # ----------------------------------------------------------------------- + device_vendor_support: + type: string + description: "Vendor support contract information" + required: false + example: "Fortinet ProSupport - Contract #12345" + + device_rack_position: + type: string + description: "Physical rack location" + required: false + example: "RAL-Rack01-U1" + + device_notes: + type: string + description: "Additional device notes" + required: false + example: "Primary datacenter switch" + +# ============================================================================= +# ACCOUNTS +# ============================================================================= + +accounts: + # Local Administrator Accounts + account_local_admin_username: + type: string + description: "Local administrator username" + required: true + example: "osfadmin" + + account_local_admin_password: + type: string + description: "Local admin password (Key Vault reference)" + required: true + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-mgmt/local-admin-password" + + account_linux_admin_username: + type: string + description: "Linux local administrator username" + required: false + default: "linuxadmin" + + account_linux_admin_password: + type: string + description: "Linux administrator password (Key Vault reference)" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-mgmt/linux-admin-password" + + account_linux_admin_ssh_public_key: + type: string + description: "Linux administrator SSH public key" + required: false + example: "ssh-rsa AAAAB3NzaC1yc2E..." + + linux_admin_ssh_private_key: + type: string + description: "Linux administrator SSH private key (Key Vault reference)" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-mgmt/linux-admin-ssh-private-key" + + # Active Directory Service Accounts + account_ad_domain_admin_username: + type: string + description: "Domain admin username" + required: false + example: "AZLOCAL\\Administrator" + + account_ad_domain_admin_password: + type: string + description: "Domain admin password (Key Vault reference)" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-mgmt/domain-admin-password" + + account_lcm_username: + type: string + description: "Azure Local Lifecycle Manager (LCM) deployment account sAMAccountName — bare username only, do NOT include @domain (the domain is specified separately via domainFqdn)" + required: false + example: "svc-azlocal-lcm" + + account_lcm_password: + type: string + description: "Azure Local LCM deployment account password (Key Vault reference)" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-mgmt/lcm-deployment-password" + + # Service Principals + spn_automation_name: + category: "identity" + subcategory: "service-principal" + type: string + description: "Automation service principal name" + required: false + example: "IIC-CMP-Automation-SPN" + + spn_automation_client_id: + category: "identity" + subcategory: "service-principal" + type: string + description: "Automation SPN client ID" + required: false + format: uuid + sensitive: true + example: "00000000-aaaa-bbbb-cccc-333333333333" + + spn_automation_password: + type: string + description: "Automation SPN password (Key Vault reference)" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-mgmt/automation-spn-secret" + + # Azure Local Deployment Service Principals + spn_azl_deployment_name: + category: "identity" + subcategory: "service-principal" + type: string + description: "Azure Local cluster deployment service principal name (used for ARM template deployments)" + required: false + example: "AzureLocal-Deployment-SPN" + + spn_azl_deployment_client_id: + category: "identity" + subcategory: "service-principal" + type: string + description: "Azure Local deployment SPN client ID (app ID)" + required: false + format: uuid + sensitive: true + example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + + spn_azl_deployment_object_id: + category: "identity" + subcategory: "service-principal" + type: string + description: "Azure Local deployment SPN object ID (for RBAC assignments)" + required: false + format: uuid + example: "12345678-abcd-ef12-3456-789012345678" + + spn_azl_deployment_secret: + category: "identity" + subcategory: "service-principal" + type: string + description: "Azure Local deployment SPN secret (Key Vault reference)" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-mgmt/azl-deployment-spn-secret" + + # Azure Local Onboarding Service Principals + spn_azl_onboarding_name: + category: "identity" + subcategory: "service-principal" + type: string + description: "Azure Local tenant onboarding service principal name (used for cross-tenant setup)" + required: false + example: "AzureLocal-Onboarding-SPN" + + spn_azl_onboarding_client_id: + category: "identity" + subcategory: "service-principal" + type: string + description: "Azure Local onboarding SPN client ID (app ID)" + required: false + format: uuid + sensitive: true + example: "b2c3d4e5-f6a7-8901-bcde-f12345678901" + + spn_azl_onboarding_object_id: + category: "identity" + subcategory: "service-principal" + type: string + description: "Azure Local onboarding SPN object ID (for RBAC assignments)" + required: false + format: uuid + example: "23456789-bcde-f123-4567-890123456789" + + spn_azl_onboarding_secret: + category: "identity" + subcategory: "service-principal" + type: string + description: "Azure Local onboarding SPN secret (Key Vault reference)" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-mgmt/azl-onboarding-spn-secret" + + # Entra ID Security Groups + entraid_group_keyvault_admins: + category: "identity" + subcategory: "security-group" + type: string + description: "Entra ID security group for Key Vault administrators (object ID or display name)" + required: false + example: "azlc-key-vault-admins" + note: "Group members have full access to Key Vault secrets, keys, and certificates" + + entraid_group_keyvault_readers: + category: "identity" + subcategory: "security-group" + type: string + description: "Entra ID security group for Key Vault readers (object ID or display name)" + required: false + example: "azlc-key-vault-readers" + note: "Group members have read-only access to Key Vault secrets" + + entraid_group_subscription_owners: + category: "identity" + subcategory: "security-group" + type: string + description: "Entra ID security group for subscription owners (object ID or display name)" + required: false + example: "azlc-subscription-owners" + note: "Group members have Owner role at subscription scope" + + entraid_group_subscription_contributors: + category: "identity" + subcategory: "security-group" + type: string + description: "Entra ID security group for subscription contributors (object ID or display name)" + required: false + example: "azlc-subscription-contributors" + note: "Group members have Contributor role at subscription scope" + + entraid_group_subscription_readers: + category: "identity" + subcategory: "security-group" + type: string + description: "Entra ID security group for subscription readers (object ID or display name)" + required: false + example: "azlc-subscription-readers" + note: "Group members have Reader role at subscription scope" + + entraid_group_pim_eligible_admins: + category: "identity" + subcategory: "security-group" + type: string + description: "Entra ID security group for PIM-eligible administrators (object ID or display name)" + required: false + example: "azlc-pim-eligible-admins" + note: "Group members can activate privileged roles via PIM (just-in-time access)" + + entraid_group_pim_permanent_admins: + category: "identity" + subcategory: "security-group" + type: string + description: "Entra ID security group for PIM permanent administrators (object ID or display name)" + required: false + example: "azlc-pim-permanent-admins" + note: "Group members have permanent privileged access (use sparingly)" + + entraid_group_azl_operators: + category: "identity" + subcategory: "security-group" + type: string + description: "Entra ID security group for Azure Local cluster operators (object ID or display name)" + required: false + example: "azlc-operators" + note: "Group members can manage Azure Local clusters and perform day-to-day operations" + + entraid_group_azl_deployment_admins: + category: "identity" + subcategory: "security-group" + type: string + description: "Entra ID security group for Azure Local deployment administrators (object ID or display name)" + required: false + example: "azlc-deployment-admins" + note: "Group members can deploy and configure Azure Local clusters" + + entraid_group_b2b_admins: + category: "identity" + subcategory: "security-group" + type: string + description: "Entra ID security group for B2B/cross-tenant administrators (object ID or display name)" + required: false + example: "azlc-b2b-env-admins" + note: "Group members can manage B2B invitations and cross-tenant access policies" + + entraid_group_network_admins: + category: "identity" + subcategory: "security-group" + type: string + description: "Entra ID security group for network administrators (object ID or display name)" + required: false + example: "azlc-network-admins" + note: "Group members can manage virtual networks, NSGs, and network resources" + + entraid_group_security_admins: + category: "identity" + subcategory: "security-group" + type: string + description: "Entra ID security group for security administrators (object ID or display name)" + required: false + example: "azlc-security-admins" + note: "Group members can manage security policies, Azure Policy, and compliance" + + entraid_group_monitoring_readers: + category: "identity" + subcategory: "security-group" + type: string + description: "Entra ID security group for monitoring readers (object ID or display name)" + required: false + example: "azlc-monitoring-readers" + note: "Group members can view monitoring data, logs, and alerts" + + entraid_group_automation_accounts: + category: "identity" + subcategory: "security-group" + type: string + description: "Entra ID security group for automation service accounts (object ID or display name)" + required: false + example: "azlc-automation-accounts" + note: "Group for service principals and automation accounts with managed access" + + entraid_group_backup_operators: + category: "identity" + subcategory: "security-group" + type: string + description: "Entra ID security group for backup operators (object ID or display name)" + required: false + example: "azlc-backup-operators" + note: "Group members can manage backup and recovery operations" + + # Azure Local Deployment RBAC Roles + # Required roles for Azure Local (Azure Stack HCI) deployments via portal or ARM templates + # Reference: https://learn.microsoft.com/azure-stack/hci/deploy/deployment-prerequisites + + azl_rbac_deployment_principals: + category: "identity" + subcategory: "rbac" + type: array + description: "Principals (users/groups/SPNs) requiring Azure Local deployment permissions" + required: false + items: + type: object + required: + - principal_id + - principal_type + properties: + principal_id: + type: string + description: "Object ID of user, group, or service principal" + format: uuid + principal_type: + type: string + description: "Type of principal" + enum: ["User", "Group", "ServicePrincipal"] + display_name: + type: string + description: "Display name for reference" + example: + - principal_id: "00000000-aaaa-bbbb-cccc-333333333333" + principal_type: "ServicePrincipal" + display_name: "AzureLocal-Deployment-SPN" + - principal_id: "12345678-1234-1234-1234-123456789012" + principal_type: "Group" + display_name: "azlc-deployment-admins" + + azl_rbac_required_roles_subscription: + category: "identity" + subcategory: "rbac" + type: array + description: "Required RBAC roles at subscription scope for Azure Local deployment" + required: true + items: + type: string + default: + - "Azure Stack HCI Administrator" + - "Reader" + - "Key Vault Secrets User" + - "Key Vault Contributor" + - "Storage Account Contributor" + example: + - "Azure Stack HCI Administrator" + - "Reader" + - "Key Vault Secrets User" + - "Key Vault Contributor" + - "Storage Account Contributor" + note: "These roles must be assigned at subscription level for deployment user/SPN" + + azl_rbac_required_roles_resource_group: + category: "identity" + subcategory: "rbac" + type: array + description: "Required RBAC roles at resource group scope for Azure Local deployment" + required: false + items: + type: string + default: + - "Contributor" + - "Key Vault Secrets Officer" + example: + - "Contributor" + - "Key Vault Secrets Officer" + note: "These roles must be assigned at resource group level for deployment resources" + + azl_rbac_custom_role_arc_registration: + category: "identity" + subcategory: "rbac" + type: object + description: "Custom role definition for Azure Arc machine registration (required for cluster nodes)" + required: false + properties: + name: + type: string + description: "Custom role name" + example: "Azure Stack HCI registration role - custom" + description: + type: string + description: "Role description" + permissions: + type: array + description: "Required permissions for Arc registration" + items: + type: string + default: + - "Microsoft.HybridCompute/machines/read" + - "Microsoft.HybridCompute/machines/write" + - "Microsoft.HybridCompute/machines/delete" + - "Microsoft.GuestConfiguration/guestConfigurationAssignments/read" + example: + name: "Azure Stack HCI registration role - custom" + description: "Custom role for Arc-enabling cluster nodes" + permissions: + - "Microsoft.HybridCompute/machines/read" + - "Microsoft.HybridCompute/machines/write" + - "Microsoft.HybridCompute/machines/delete" + - "Microsoft.GuestConfiguration/guestConfigurationAssignments/read" + note: "Required for Arc-enabling cluster nodes during deployment" + + azl_rbac_role_assignments: + category: "identity" + subcategory: "rbac" + type: array + description: "Complete RBAC role assignments for Azure Local deployment (declarative model)" + required: false + items: + type: object + required: + - principal_id + - role_definition_name + - scope + properties: + principal_id: + type: string + description: "Object ID of principal (user/group/SPN)" + format: uuid + principal_type: + type: string + description: "Principal type" + enum: ["User", "Group", "ServicePrincipal"] + default: "ServicePrincipal" + role_definition_name: + type: string + description: "Built-in or custom role name" + example: "Azure Stack HCI Administrator" + role_definition_id: + type: string + description: "Role definition resource ID (optional, use name instead)" + format: azure_resource_id + scope: + type: string + description: "Assignment scope (subscription, resource group, or resource)" + format: azure_resource_id + description: + type: string + description: "Assignment description/justification" + condition: + type: string + description: "Role assignment condition (ABAC)" + examples: + minimal: + # Simplest - SPN with required roles at subscription + - principal_id: "00000000-aaaa-bbbb-cccc-333333333333" + principal_type: "ServicePrincipal" + role_definition_name: "Azure Stack HCI Administrator" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444" + description: "Deployment SPN - HCI Administrator" + - principal_id: "00000000-aaaa-bbbb-cccc-333333333333" + principal_type: "ServicePrincipal" + role_definition_name: "Key Vault Secrets User" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444" + description: "Deployment SPN - Key Vault access" + + standard: + # Production - SPN and admin group with full permissions + # Deployment SPN roles + - principal_id: "00000000-aaaa-bbbb-cccc-333333333333" + principal_type: "ServicePrincipal" + role_definition_name: "Azure Stack HCI Administrator" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444" + description: "Deployment SPN - HCI Administrator at subscription" + - principal_id: "00000000-aaaa-bbbb-cccc-333333333333" + principal_type: "ServicePrincipal" + role_definition_name: "Reader" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444" + description: "Deployment SPN - Reader at subscription" + - principal_id: "00000000-aaaa-bbbb-cccc-333333333333" + principal_type: "ServicePrincipal" + role_definition_name: "Key Vault Secrets User" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-identity-mgmt/providers/Microsoft.KeyVault/vaults/kv-azlc-mgmt" + description: "Deployment SPN - Key Vault secrets access" + - principal_id: "00000000-aaaa-bbbb-cccc-333333333333" + principal_type: "ServicePrincipal" + role_definition_name: "Contributor" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-landingzone-cluster" + description: "Deployment SPN - Contributor at cluster resource group" + + # Admin group roles + - principal_id: "12345678-1234-1234-1234-123456789012" + principal_type: "Group" + role_definition_name: "Azure Stack HCI Administrator" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444" + description: "Deployment admins group - HCI Administrator" + - principal_id: "12345678-1234-1234-1234-123456789012" + principal_type: "Group" + role_definition_name: "Key Vault Administrator" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-identity-mgmt/providers/Microsoft.KeyVault/vaults/kv-azlc-mgmt" + description: "Deployment admins group - Key Vault admin" + + complete: + # Enterprise - Full RBAC with conditions and all required roles + # Deployment SPN - Subscription level + - principal_id: "00000000-aaaa-bbbb-cccc-333333333333" + principal_type: "ServicePrincipal" + role_definition_name: "Azure Stack HCI Administrator" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444" + description: "Deployment SPN - Azure Stack HCI Administrator (required)" + - principal_id: "00000000-aaaa-bbbb-cccc-333333333333" + principal_type: "ServicePrincipal" + role_definition_name: "Reader" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444" + description: "Deployment SPN - Reader for resource discovery" + - principal_id: "00000000-aaaa-bbbb-cccc-333333333333" + principal_type: "ServicePrincipal" + role_definition_name: "Storage Account Contributor" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444" + description: "Deployment SPN - Storage Account management for cloud witness" + + # Deployment SPN - Resource group level + - principal_id: "00000000-aaaa-bbbb-cccc-333333333333" + principal_type: "ServicePrincipal" + role_definition_name: "Contributor" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-landingzone-cluster" + description: "Deployment SPN - Contributor at cluster resource group" + + # Deployment SPN - Key Vault specific + - principal_id: "00000000-aaaa-bbbb-cccc-333333333333" + principal_type: "ServicePrincipal" + role_definition_name: "Key Vault Secrets User" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-identity-mgmt/providers/Microsoft.KeyVault/vaults/kv-azlc-mgmt" + description: "Deployment SPN - Read secrets (domain credentials, SPN secrets)" + - principal_id: "00000000-aaaa-bbbb-cccc-333333333333" + principal_type: "ServicePrincipal" + role_definition_name: "Key Vault Contributor" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-identity-mgmt/providers/Microsoft.KeyVault/vaults/kv-azlc-mgmt" + description: "Deployment SPN - Manage Key Vault (create secrets during deployment)" + + # Deployment admins group - Subscription level + - principal_id: "12345678-1234-1234-1234-123456789012" + principal_type: "Group" + role_definition_name: "Azure Stack HCI Administrator" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444" + description: "Deployment admins - HCI Administrator for portal/ARM deployments" + - principal_id: "12345678-1234-1234-1234-123456789012" + principal_type: "Group" + role_definition_name: "User Access Administrator" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444" + description: "Deployment admins - Manage RBAC assignments" + condition: "@Resource[Microsoft.Authorization/roleAssignments:PrincipalType] StringEquals 'ServicePrincipal'" + + # Deployment admins group - Key Vault + - principal_id: "12345678-1234-1234-1234-123456789012" + principal_type: "Group" + role_definition_name: "Key Vault Administrator" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-identity-mgmt/providers/Microsoft.KeyVault/vaults/kv-azlc-mgmt" + description: "Deployment admins - Full Key Vault access" + + # Operators group - Read-only + - principal_id: "23456789-2345-2345-2345-234567890123" + principal_type: "Group" + role_definition_name: "Reader" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444" + description: "Operators - Read-only subscription access" + - principal_id: "23456789-2345-2345-2345-234567890123" + principal_type: "Group" + role_definition_name: "Azure Stack HCI VM Contributor" + scope: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-landingzone-cluster" + description: "Operators - Manage VMs on Azure Local cluster" + + azl_rbac_validate_before_deployment: + category: "identity" + subcategory: "rbac" + type: boolean + description: "Validate RBAC assignments before starting Azure Local deployment" + required: false + default: true + note: "Recommended to prevent deployment failures due to missing permissions" + + # Hardware Management Interfaces (iDRAC/BMC) + account_idrac_username: + type: string + description: "iDRAC/BMC username (stored in Key Vault)" + required: false + sensitive: true + example: "root" + note: "Typically 'root' for Dell iDRAC, 'Administrator' for HPE iLO" + + account_idrac_password: + type: string + description: "iDRAC/BMC password (Key Vault reference)" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-mgmt/idrac-password" + + # iDRAC VNC Remote Console Settings + idrac_vnc_enabled: + type: boolean + description: "Enable VNC on iDRAC for remote console access" + required: false + default: true + example: true + note: "Enables third-party VNC client access to iDRAC virtual console" + + idrac_vnc_port: + type: integer + description: "VNC port number for iDRAC virtual console" + required: false + default: 5901 + example: 5901 + note: "Standard VNC port offset from 5900" + + idrac_vnc_timeout: + type: integer + description: "VNC session timeout in seconds" + required: false + default: 1800 + example: 1800 + note: "30 minutes default timeout for VNC sessions" + + idrac_vnc_password: + type: string + description: "VNC password for third-party VNC clients (8 character max)" + required: false + sensitive: true + maxLength: 8 + example: "azl2025!" + note: "Dell iDRAC VNC passwords are limited to 8 characters" + + idrac_vnc_password_secret: + type: string + description: "VNC password Key Vault reference" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-mgmt/idrac-vnc-password" + + # Cloud Witness Storage Account Key + cloud_witness_storage_key: + type: string + description: "Cloud witness storage account access key (Key Vault reference)" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-mgmt/cloud-witness-storage-key" + +# ============================================================================= +# ENVIRONMENT +# ============================================================================= + +environment: + env_name: + type: string + description: "Environment identifier" + required: true + allowedValues: + - azurelocalcloud + - iic-production + - iic-reference + - iic-poc + - iic-productlabs + example: "azurelocalcloud" + + env_type: + type: string + description: "Environment type" + required: true + allowedValues: + - management + - lab + - demo + - poc + - production + example: "management" + + env_owner: + type: string + description: "Environment owner email" + format: email + required: false + example: "platform@improbability.cloud" + + env_description: + type: string + description: "Environment description" + required: false + example: "Infinite Improbability Cloud Management Platform" + +# ============================================================================= +# SITE +# ============================================================================= + +site: + code: + type: string + description: "Site code" + required: true + maxLength: 5 + pattern: "^[A-Z]{2,5}$" + example: "RAL" + + env_name: + type: string + description: "Site name" + required: true + example: "IIC Datacenter Alpha" + + location: + type: string + description: "Physical location" + required: false + example: "Raleigh, NC" + + datacenter: + type: string + description: "Datacenter code" + required: false + example: "RAL" + + site_code: + type: string + description: "Site code" + required: true + maxLength: 5 + pattern: "^[A-Z]{2,5}$" + example: "AZL" + + site_datacenter: + type: string + description: "Site datacenter" + required: true + example: "IIC DC1" + + site_description: + type: string + description: "Site description" + required: true + example: "Azure Local cluster for IIC CMP" + + site_environment: + type: string + description: "Site environment" + required: true + example: "Production" + + site_location: + type: string + description: "Site location" + required: true + example: "Raleigh, NC" + + site_name: + type: string + description: "Site name" + required: true + example: "Azure Local Cloud" + + site_owner: + type: string + description: "Site owner" + required: true + example: "Product Technology Team" + +# ============================================================================= +# TAGS +# ============================================================================= + +tags: + Environment: + type: string + description: "Environment tag identifying the Azure Local environment" + required: true + allowedValues: + - azurelocalcloud + - iic-production + - iic-reference + - iic-poc + - iic-productlabs + example: "azurelocalcloud" + + Project: + type: string + description: "Project name tag" + required: true + allowedValues: + - "Azure Local Cloud Management" + - "Infinite Improbability Corp" + - "IIC Demos" + - "IIC POC" + - "IIC Product Labs" + example: "Infinite Improbability Cloud Management Platform" + + ManagedBy: + type: string + description: "Management tool identifier" + required: true + allowedValues: + - Terraform + - PowerShell + - Bicep + - Ansible + - "Azure CLI" + - Manual + default: "Infrastructure as Code" + + env_Owner: + type: string + description: "Owner tag" + required: true + example: "Product Technology Team" + + CostCenter: + type: string + description: "Cost center tag" + required: false + example: "IT-IIC" + + tag_architecture_version: + category: "operations" + subcategory: "tagging" + type: string + description: "Tag architecture version" + required: true + example: "v2.0" + +# ============================================================================= +# MONITORING & LOG ANALYTICS +# ============================================================================= + +monitoring: + monitoring_log_analytics_workspace_name: + category: "monitoring" + subcategory: "log-analytics" + type: string + description: "Log Analytics workspace name" + required: false + pattern: "^law-[a-z0-9-]+$" + example: "law-azlc-iic-eus-monitoring" + + monitoring_log_analytics_workspace_sku: + category: "monitoring" + subcategory: "log-analytics" + type: string + description: "Log Analytics workspace SKU" + required: false + allowedValues: + - Free + - PerGB2018 + - PerNode + - Premium + - Standard + - Standalone + default: "PerGB2018" + + monitoring_log_analytics_retention_days: + category: "monitoring" + subcategory: "retention" + type: integer + description: "Log retention in days" + required: false + minimum: 30 + maximum: 730 + default: 90 + + monitoring_agent_windows_enabled: + type: boolean + description: "Enable Azure Monitor Agent on Windows VMs" + required: false + default: true + + monitoring_agent_linux_enabled: + type: boolean + description: "Enable Azure Monitor Agent on Linux VMs" + required: false + default: true + + monitoring_application_insights_name: + type: string + description: "Application Insights name" + required: false + pattern: "^ai-[a-z0-9-]+$" + example: "ai-azlc-iic-eus-app" + + monitoring_alert_email_addresses: + type: array + description: "Email addresses for alerts" + required: false + items: + type: string + format: email + example: + - "platform@improbability.cloud" + + monitoring_alert_severity_levels: + type: array + description: "Alert severity levels to monitor" + required: false + items: + type: integer + minimum: 0 + maximum: 4 + example: + - 0 + - 1 + - 2 + + monitoring_resource_group: + type: string + description: "Monitoring resource group" + required: false + pattern: "^rg-[a-z0-9-]+$" + example: "rg-azlc-iic-eus-management-monitoring" + + monitoring_enabled: + type: boolean + description: "Enable Azure Monitor" + required: false + default: true + + monitoring_workspace: + type: string + description: "Azure Monitor workspace name" + required: false + pattern: "^amw-[a-z0-9-]+$" + example: "amw-azlc-iic-eus" + + # Metrics Configuration + monitoring_metrics_retention_days: + type: integer + description: "Metrics retention days" + required: true + example: "90" + + # Data Collection Rules (DCR) + monitoring_windows_performance_dcr_name: + type: string + description: "Windows performance data collection rule name" + required: false + pattern: "^dcr-[a-z0-9-]+$" + example: "dcr-azlc-iic-eus-winperf" + + windows_events_dcr_name: + type: string + description: "Windows events data collection rule name" + required: false + pattern: "^dcr-[a-z0-9-]+$" + example: "dcr-azlc-iic-eus-winevents" + + monitoring_performance_counter_sampling_frequency: + type: integer + description: "Performance counter sampling frequency (seconds)" + required: false + minimum: 10 + maximum: 3600 + default: 60 + + monitoring_dcr_destination_table: + type: string + description: "Log Analytics workspace table name for data collection rule output data. Use standard tables (Perf, Event, Syslog) or custom tables with _CL suffix for custom log data" + required: false + pattern: "^[A-Za-z][A-Za-z0-9_]*(_CL)?$" + example: "Perf" + + monitoring_dcr_linux_syslog_facilities: + type: array + description: "Linux syslog facilities" + required: false + items: + type: string + example: + - "[\"auth\", \"authpriv\", \"cron\"]" + + monitoring_dcr_linux_syslog_log_levels: + type: array + description: "Linux syslog log levels" + required: false + items: + type: string + example: + - "[\"Emergency\", \"Alert\", \"Critical\"]" + + monitoring_dcr_linux_syslog_name: + type: string + description: "Linux syslog DCR name" + required: false + example: "dcr-linux-syslog" + + monitoring_dcr_windows_events_logs: + type: array + description: "Windows event logs" + required: false + items: + type: string + example: + - "[\"System\", \"Application\"]" + + monitoring_dcr_windows_events_name: + type: string + description: "Windows events DCR name" + required: false + example: "dcr-windows-events" + + monitoring_dcr_windows_perf_counters: + type: array + description: "Windows performance counters" + required: false + items: + type: string + example: + - "[\"\\\\Processor(_Total)\\\\% Processor Time\"]" + + monitoring_dcr_windows_perf_name: + type: string + description: "Windows performance DCR name" + required: false + example: "dcr-windows-perf" + + # Alert Configuration + monitoring_alert_action_group_name: + type: string + description: "Alert action group name" + required: false + example: "ag-azlc-iic-alerts" + + monitoring_alert_email_receiver: + type: string + description: "Alert email receiver" + required: false + format: email + example: "" + + monitoring_alert_webhook_receiver: + type: string + description: "Alert webhook receiver" + required: false + example: "" + +# ============================================================================= +# ACTIVE DIRECTORY +# ============================================================================= + +active_directory: + ad_enabled: + category: "identity" + subcategory: "active-directory" + type: boolean + description: "Active Directory enabled" + required: false + default: true + + ad_domain: + category: "identity" + subcategory: "active-directory" + type: string + description: "Active Directory domain (short name)" + required: false + example: "azlocal" + + ad_domain_fqdn: + category: "identity" + subcategory: "active-directory" + type: string + description: "Active Directory domain FQDN" + required: false + example: "azlocal.mgmt" + + ad_netbios_name: + category: "identity" + subcategory: "active-directory" + type: string + description: "Active Directory NetBIOS name" + required: false + maxLength: 15 + pattern: "^[A-Z][A-Z0-9-]*$" + example: "MGMT" + + ad_ou_path: + category: "identity" + subcategory: "active-directory" + type: string + description: "Active Directory OU path" + required: false + example: "OU=MGMT,DC=azlocal,DC=mgmt" + + ad_computers_ou_path: + category: "identity" + subcategory: "active-directory" + type: string + description: "Active Directory computers OU path" + required: false + example: "OU=Computers,OU=MGMT,DC=azlocal,DC=mgmt" + + ad_servers_ou_path: + type: string + description: "Active Directory servers OU path" + required: false + example: "OU=Servers,OU=MGMT,DC=azlocal,DC=mgmt" + + ad_clusters_ou_path: + type: string + description: "Active Directory OU path for Azure Local cluster instance" + required: false + example: "OU=AzureLocal-Cluster01,OU=AzureLocal,OU=Clusters,OU=Clusters,OU=MGMT,DC=azlocal,DC=mgmt" + + ad_service_accounts_ou_path: + type: string + description: "Active Directory service accounts OU path" + required: false + example: "OU=ServiceAccounts,OU=MGMT,DC=azlocal,DC=mgmt" + + ad_dns_servers: + type: array + description: "Active Directory DNS server IP addresses" + required: true + items: + type: string + format: ipv4 + example: + - "10.100.1.36" + - "10.100.1.37" + + ad_dns_server_fqdns: + type: array + description: "Active Directory DNS server FQDNs (for NTP, replication, etc.)" + required: false + items: + type: string + example: + - "azlc-dc01.azlocal.cloud" + - "azlc-dc02.azlocal.cloud" + + ad_group_azure_local_admins: + type: string + description: "Azure Local admins group" + required: false + example: "AZLOCAL\\AzureLocalAdmins" + + ad_group_iic_admins: + type: string + description: "IIC admins group" + required: false + example: "AZLOCAL\\IICAdmins" + + ad_group_wac_admins: + type: string + description: "WAC admins group" + required: false + example: "AZLOCAL\\WACAdmins" + + ad_domain_controller_01_name: + type: string + description: "Active Directory Domain Controller 01 hostname" + required: false + maxLength: 15 + example: "AZLC-DC01" + + ad_domain_controller_02_name: + type: string + description: "Active Directory Domain Controller 02 hostname" + required: false + maxLength: 15 + example: "AZLC-DC02" + +# ============================================================================= +# AZURE POLICY +# ============================================================================= + +policy: + azure_policy_assignment_scope: + type: string + description: "Policy assignment scope" + required: false + example: "/providers/Microsoft.Management/managementGroups/mg-azlc-iic" + + azure_policy_effect_type: + type: string + description: "Policy effect type" + required: false + allowedValues: + - Audit + - Deny + - DeployIfNotExists + - Disabled + default: "Audit" + + dine_policies_enabled: + type: boolean + description: "Deploy If Not Exists policies enabled" + required: false + default: true + + custom_policy_definitions: + type: array + description: "Custom policy definitions" + required: false + items: + type: object + properties: + name: + type: string + display_name: + type: string + mode: + type: string + example: + - name: "require-tags" + display_name: "Require specified tags" + mode: "Indexed" + + azure_policy_assignment_scope_platform: + type: string + description: "Policy assignment scope platform" + required: true + example: "/providers/Microsoft.Management/managementGroups/platform-identity" + + azure_policy_assignment_scope_primary: + type: string + description: "Policy assignment scope primary" + required: true + example: "Management Group" + + azure_policy_assignment_scope_workload: + type: string + description: "Policy assignment scope workload" + required: true + example: "/subscriptions/00000000-1111-2222-3333-444444444444" + + azure_policy_azurelocal_encryption: + type: boolean + description: "Policy Azure Local encryption" + required: true + example: "true" + + azure_policy_azurelocal_network_protection: + type: boolean + description: "Policy Azure Local network protection" + required: true + example: "true" + + azure_policy_azurelocal_secured_core: + type: boolean + description: "Policy Azure Local secured core" + required: true + example: "true" + + azure_policy_azurelocal_wdac_enforcement: + type: boolean + description: "Policy Azure Local WDAC enforcement" + required: true + example: "true" + + azure_policy_custom_initiatives_used: + type: boolean + description: "Policy custom initiatives used" + required: true + example: "true" + + azure_policy_defender_cloud_enablement_assignment: + type: boolean + description: "Policy defender cloud enablement assignment" + required: true + example: "true" + + azure_policy_defender_cloud_enablement_subscription: + type: boolean + description: "Policy defender cloud enablement subscription" + required: true + example: "true" + + azure_policy_defender_email_notifications: + type: boolean + description: "Policy defender email notifications" + required: true + example: "true" + + azure_policy_defender_workflow_automation: + type: boolean + description: "Policy defender workflow automation" + required: true + example: "true" + + azure_policy_dine_policies_enabled: + type: boolean + description: "Policy DINE policies enabled" + required: true + example: "true" + + azure_policy_exemption_approval_authority: + type: string + description: "Policy exemption approval authority" + required: true + example: "Security Team" + + azure_policy_exemption_process_documented: + type: boolean + description: "Policy exemption process documented" + required: true + example: "true" + + azure_policy_initiative_assignment_mg_level: + type: boolean + description: "Policy initiative assignment MG level" + required: true + example: "true" + + azure_policy_modify_policies_avoided: + type: boolean + description: "Policy modify policies avoided" + required: true + example: "true" + + azure_policy_preferred_effect_type: + type: string + description: "Policy preferred effect type" + required: true + example: "Deny" + + azure_policy_update_manager_periodic_assessment: + type: boolean + description: "Policy update manager periodic assessment" + required: true + example: "true" + + azure_policy_update_manager_scheduled_patching: + type: boolean + description: "Policy update manager scheduled patching" + required: true + example: "true" + + azure_policy_vulnerability_assessment_required: + type: boolean + description: "Policy vulnerability assessment required" + required: true + example: "true" + + # Azure Backup & Recovery Services Policies + azure_policy_backup_enabled_for_vms: + type: boolean + description: "Policy to require Azure Backup for virtual machines" + required: false + default: true + + azure_policy_backup_retention_enforcement: + type: boolean + description: "Policy to enforce backup retention requirements" + required: false + default: true + + azure_policy_site_recovery_enabled: + type: boolean + description: "Policy to enable Azure Site Recovery for disaster recovery" + required: false + default: false + + # Key Vault Policies + azure_policy_keyvault_firewall_enabled: + type: boolean + description: "Policy to require Key Vault firewall/network rules" + required: false + default: true + + azure_policy_keyvault_soft_delete_enabled: + type: boolean + description: "Policy to require Key Vault soft delete" + required: false + default: true + + azure_policy_keyvault_purge_protection_enabled: + type: boolean + description: "Policy to require Key Vault purge protection" + required: false + default: true + + azure_policy_keyvault_rbac_authorization: + type: boolean + description: "Policy to enforce RBAC authorization for Key Vault" + required: false + default: true + + azure_policy_keyvault_private_endpoint_required: + type: boolean + description: "Policy to require private endpoint for Key Vault" + required: false + default: true + + # Storage Account Policies + azure_policy_storage_secure_transfer_required: + type: boolean + description: "Policy to require secure transfer (HTTPS) for storage accounts" + required: false + default: true + + azure_policy_storage_minimum_tls_version: + type: string + description: "Policy to enforce minimum TLS version for storage" + required: false + allowedValues: + - TLS1_2 + - TLS1_3 + default: "TLS1_2" + + azure_policy_storage_public_access_disabled: + type: boolean + description: "Policy to disable public blob access on storage accounts" + required: false + default: true + + azure_policy_storage_private_endpoint_required: + type: boolean + description: "Policy to require private endpoint for storage accounts" + required: false + default: true + + azure_policy_storage_infrastructure_encryption: + type: boolean + description: "Policy to require infrastructure encryption for storage" + required: false + default: true + + # Virtual Machine Policies + azure_policy_vm_backup_required: + type: boolean + description: "Policy to require backup for virtual machines" + required: false + default: true + + azure_policy_vm_disk_encryption_required: + type: boolean + description: "Policy to require disk encryption for VMs" + required: false + default: true + + azure_policy_vm_diagnostics_required: + type: boolean + description: "Policy to require diagnostic settings for VMs" + required: false + default: true + + azure_policy_vm_azure_monitor_agent_required: + type: boolean + description: "Policy to require Azure Monitor Agent on VMs" + required: false + default: true + + azure_policy_vm_antimalware_extension_required: + type: boolean + description: "Policy to require antimalware extension on Windows VMs" + required: false + default: true + + azure_policy_vm_approved_images_only: + type: boolean + description: "Policy to allow only approved VM images" + required: false + default: false + + # Network Policies + azure_policy_nsg_flow_logs_enabled: + type: boolean + description: "Policy to enable NSG flow logs" + required: false + default: true + + azure_policy_network_watcher_enabled: + type: boolean + description: "Policy to enable Network Watcher in all regions" + required: false + default: true + + azure_policy_ddos_protection_standard: + type: boolean + description: "Policy to require DDoS Protection Standard for VNets" + required: false + default: false + + azure_policy_vnet_peering_restrictions: + type: boolean + description: "Policy to restrict VNet peering to approved networks" + required: false + default: false + + azure_policy_subnet_nsg_required: + type: boolean + description: "Policy to require NSG on all subnets" + required: false + default: true + + # Azure Bastion Policies + azure_policy_bastion_required_for_jumpbox: + type: boolean + description: "Policy to require Azure Bastion for jumpbox access" + required: false + default: true + + azure_policy_bastion_sku_standard: + type: boolean + description: "Policy to require Standard SKU for Azure Bastion" + required: false + default: false + + # Monitoring & Diagnostics Policies + azure_policy_diagnostic_settings_required: + type: boolean + description: "Policy to require diagnostic settings on all resources" + required: false + default: true + + azure_policy_log_analytics_workspace_required: + type: boolean + description: "Policy to require Log Analytics workspace deployment" + required: false + default: true + + azure_policy_activity_log_retention_days: + type: integer + description: "Policy to enforce activity log retention period (days)" + required: false + minimum: 90 + maximum: 730 + default: 90 + + # Resource Tagging Policies + azure_policy_required_tags: + type: array + description: "List of required tags for all resources" + required: false + items: + type: string + example: + - "Environment" + - "Project" + - "Owner" + - "CostCenter" + + azure_policy_tag_inheritance_enabled: + type: boolean + description: "Policy to inherit tags from resource group" + required: false + default: true + + # Cost Management Policies + azure_policy_budget_alerts_enabled: + type: boolean + description: "Policy to enable budget alerts" + required: false + default: true + + azure_policy_unused_resources_cleanup: + type: boolean + description: "Policy to identify and tag unused resources" + required: false + default: false + +# ============================================================================= +# AZURE UPDATE MANAGER +# ============================================================================= + +azure_update_manager: + # Update Management + update_enabled: + type: boolean + description: "Enable Azure Update Manager for the environment" + required: false + default: true + + # Maintenance Configuration + update_maintenance_configuration_name: + type: string + description: "Azure maintenance configuration name" + required: false + pattern: "^maint-[a-z0-9-]+$" + example: "maint-azlc-iic-weekly" + + update_maintenance_configuration_resource_group: + type: string + description: "Resource group for maintenance configuration" + required: false + pattern: "^rg-[a-z0-9-]+$" + example: "rg-azlc-iic-eus-management" + + # Schedule Configuration + update_maintenance_schedule_frequency: + type: string + description: "Update schedule frequency" + required: false + allowedValues: + - Daily + - Weekly + - Monthly + default: "Weekly" + + update_maintenance_schedule_day: + type: string + description: "Day of week for scheduled maintenance (Weekly/Monthly)" + required: false + allowedValues: + - Monday + - Tuesday + - Wednesday + - Thursday + - Friday + - Saturday + - Sunday + - "First Sunday" + - "Second Sunday" + - "Third Sunday" + - "Fourth Sunday" + - "Last Sunday" + example: "Sunday" + + update_maintenance_schedule_time: + type: string + description: "Time of day for scheduled maintenance (HH:MM 24-hour format)" + required: false + pattern: "^([01][0-9]|2[0-3]):[0-5][0-9]$" + example: "02:00" + + update_maintenance_window_duration_hours: + type: integer + description: "Maintenance window duration in hours" + required: false + minimum: 1 + maximum: 5 + default: 3 + + # Update Classifications - Windows + update_classifications_windows: + type: array + description: "Windows update classifications to include" + required: false + items: + type: string + allowedValues: + - Critical + - Security + - UpdateRollup + - FeaturePack + - ServicePack + - Definition + - Tools + - Updates + default: + - Critical + - Security + - UpdateRollup + + # Update Classifications - Linux + update_classifications_linux: + type: array + description: "Linux update classifications to include" + required: false + items: + type: string + allowedValues: + - Critical + - Security + - Other + default: + - Critical + - Security + + # KB Number Control + update_kb_numbers_to_exclude: + type: array + description: "Windows KB numbers to exclude from updates" + required: false + items: + type: string + pattern: "^KB[0-9]{7}$" + example: + - "KB5001234" + - "KB5005678" + + update_kb_numbers_to_include: + type: array + description: "Windows KB numbers to explicitly include (overrides classifications)" + required: false + items: + type: string + pattern: "^KB[0-9]{7}$" + example: + - "KB5009876" + + # Reboot Configuration + update_reboot_setting: + type: string + description: "Reboot behavior after updates" + required: false + allowedValues: + - IfRequired + - Never + - Always + default: "IfRequired" + + # Pre/Post Maintenance Tasks + pre_maintenance_tasks_enabled: + type: boolean + description: "Enable pre-maintenance task execution" + required: false + default: false + + post_maintenance_tasks_enabled: + type: boolean + description: "Enable post-maintenance task execution" + required: false + default: false + + # Assessment Configuration + update_periodic_assessment_enabled: + type: boolean + description: "Enable periodic assessment of update compliance" + required: false + default: true + + update_assessment_mode: + type: string + description: "Update assessment mode" + required: false + allowedValues: + - ImageDefault + - AutomaticByPlatform + default: "AutomaticByPlatform" + + # Patch Orchestration + patch_orchestration_mode: + type: string + description: "Patch orchestration mode for VMs" + required: false + allowedValues: + - AutomaticByPlatform + - AutomaticByOS + - Manual + default: "AutomaticByPlatform" + + # Notification Settings + patch_approver_email: + type: string + description: "Email address for patch approval notifications" + required: false + pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" + example: "admin@azlocal.cloud" + + patch_notification_lead_time_days: + type: integer + description: "Days before maintenance window to send notifications" + required: false + minimum: 1 + maximum: 30 + default: 7 + + # Azure Arc Integration + arc_enabled_servers_included: + type: boolean + description: "Include Azure Arc-enabled servers in update management" + required: false + default: true + +# ============================================================================= +# BACKUP & DISASTER RECOVERY +# ============================================================================= + +bcdr_backup_recovery: + # Recovery Services Vault + bcdr_vault_name: + category: "operations" + subcategory: "backup" + type: string + description: "Recovery Services vault name" + required: false + pattern: "^rsv-[a-z0-9-]+$" + example: "rsv-azlc-iic-eus-backup" + + bcdr_vault_resource_group: + category: "operations" + subcategory: "backup" + type: string + description: "Recovery vault resource group" + required: true + example: "rg-azlc-iic-eus-backup" + + bcdr_vault_sku: + type: string + description: "Recovery vault SKU" + required: true + example: "Standard" + + # Backup Configuration + bcdr_backup_policy_name: + category: "operations" + subcategory: "backup" + type: string + description: "Backup policy name" + required: false + example: "policy-daily-backup" + + bcdr_backup_retention_days: + category: "operations" + subcategory: "backup" + type: integer + description: "Backup retention in days" + required: false + minimum: 7 + maximum: 9999 + default: 30 + + bcdr_backup_offsite_location: + type: string + description: "Backup offsite location" + required: true + example: "West US 2" + + bcdr_backup_solution: + type: string + description: "Backup solution" + required: true + example: "Azure Backup" + + bcdr_backup_svc_password_secret: + type: string + description: "Backup service password secret" + required: true + sensitive: true + example: "(secret value)" + + # Site Recovery + bcdr_site_recovery_enabled: + type: boolean + description: "Azure Site Recovery enabled" + required: false + default: false + + # Soft Delete + bcdr_soft_delete_retention_days: + type: integer + description: "Soft delete retention days" + required: true + example: "90" + +# ============================================================================= +# SCVMM INTEGRATION +# ============================================================================= + +scvmm: + infrastructure_type: ["arc_scvmm"] + scvmm_server_name: + type: string + description: "SCVMM server name" + required: false + example: "scvmm01.azlocal.mgmt" + + scvmm_cloud_name: + type: string + description: "SCVMM cloud name" + required: false + example: "AzureLocalCloud" + + scvmm_arc_enabled: + type: boolean + description: "SCVMM Arc integration enabled" + required: false + default: false + + scvmm_port: + type: integer + description: "SCVMM server port" + required: false + minimum: 1 + maximum: 65535 + default: 8100 + + scvmm_library_share_path: + type: string + description: "UNC path to SCVMM library share for VM templates, ISOs, VHDs" + required: false + pattern: "^\\\\\\\\[a-zA-Z0-9.-]+\\\\[a-zA-Z0-9_$-]+$" + example: "\\\\scvmm-server\\Library" + note: "Library share stores VM templates, ISOs, VHDs" + + scvmm_run_as_account: + type: string + description: "SCVMM Run As account for automation (service account with SCVMM Administrator role)" + required: false + example: "domain\\scvmm-svc" + + scvmm_use_ssl: + type: boolean + description: "Use SSL for SCVMM server connection (recommended for secure communication)" + required: false + default: true + +# ============================================================================= +# AZURE ARC RESOURCE BRIDGE +# ============================================================================= +# Arc Resource Bridge for SCVMM integration only (per user scope) +# Reference: https://learn.microsoft.com/azure/azure-arc/system-center-virtual-machine-manager/overview +# Supports VMM 2019/2022/2025 with up to 15,000 VMs +# ============================================================================= + +arc_resource_bridge: + infrastructure_type: ["arc_scvmm"] + arb_scvmm_enabled: + type: boolean + description: "Deploy Arc Resource Bridge for SCVMM integration (required for Azure Arc-enabled SCVMM)" + required: false + default: false + note: "Supports VMM 2019/2022/2025, up to 15,000 VMs. Kubernetes-based virtual appliance." + reference: "https://learn.microsoft.com/azure/azure-arc/system-center-virtual-machine-manager/overview" + + arb_scvmm_name: + type: string + description: "Name of Arc Resource Bridge appliance VM" + required: false + pattern: "^[a-z0-9-]{3,24}$" + default: "arc-resource-bridge" + example: "arb-scvmm-prod-01" + + arb_scvmm_resource_group: + type: string + description: "Azure resource group for Arc Resource Bridge resources" + required: false + default: "rg-arc-resource-bridge" + example: "rg-azlc-iic-eus-arc-scvmm" + + arb_scvmm_location: + type: string + description: "Azure region for Arc Resource Bridge metadata (20+ regions supported)" + required: false + allowedValues: + - East US + - East US 2 + - West US 2 + - West US 3 + - Central US + - North Europe + - West Europe + - UK South + - Southeast Asia + - Australia East + default: "East US" + + arb_scvmm_custom_location_name: + type: string + description: "Custom location name mapping to SCVMM cloud (represents connection to specific SCVMM cloud)" + required: false + default: "scvmm-custom-location" + example: "scvmm-production-cloud" + + # Service Principal Configuration + arb_service_principal_app_id: + category: "identity" + subcategory: "arc-resource-bridge" + type: string + description: "Arc Resource Bridge service principal application ID" + required: false + format: uuid + example: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + + arb_service_principal_object_id: + category: "identity" + subcategory: "arc-resource-bridge" + type: string + description: "Arc Resource Bridge service principal object ID" + required: false + format: uuid + example: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + + hci_resource_provider_object_id: + category: "identity" + subcategory: "arc-resource-bridge" + type: string + description: "Azure Stack HCI resource provider object ID" + required: false + format: uuid + example: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + note: "Required for RBAC assignments during cluster deployment" + +# ============================================================================= +# AZURE ARC HYBRID VM MANAGEMENT +# ============================================================================= +# Arc-enabled servers for Hyper-V VMs, VMware VMs, and physical servers +# NOT for Azure Local cluster VMs (per user scope) +# Reference: https://learn.microsoft.com/azure/azure-arc/servers/overview +# ============================================================================= + +arc_hybrid_vms: + infrastructure_type: ["arc_hybrid_vms"] + # Arc Onboarding Configuration + arc_onboard_hyperv_vms: + type: boolean + description: "Enable Arc onboarding for standalone Hyper-V VMs (NOT Azure Local cluster VMs)" + required: false + default: false + note: "Installs Connected Machine agent for Hyper-V VMs not managed by Azure Local cluster" + reference: "https://learn.microsoft.com/azure/azure-arc/servers/overview" + + arc_onboard_vmware_vms: + type: boolean + description: "Enable Arc onboarding for VMware vSphere VMs" + required: false + default: false + note: "Requires Arc Resource Bridge for vSphere (separate from SCVMM). Supports vSphere 7.0/8.0, up to 9,500 VMs." + reference: "https://learn.microsoft.com/azure/azure-arc/vmware-vsphere/overview" + + arc_onboard_physical_servers: + type: boolean + description: "Enable Arc onboarding for physical servers (bare-metal Windows Server or Linux)" + required: false + default: false + + # Arc VM Extensions + arc_vm_extensions: + type: array + description: "VM extensions to deploy on Arc-enabled machines" + required: false + default: [] + allowedValues: + - MicrosoftMonitoringAgent + - AzureMonitorAgent + - DependencyAgent + - AzurePolicyforWindows + - AzurePolicyforLinux + - CustomScriptExtension + - DSC + example: + - AzureMonitorAgent + - DependencyAgent + - AzurePolicyforWindows + + # Arc Management Services + arc_update_management_enabled: + type: boolean + description: "Enable Azure Update Manager for Arc-enabled machines (manage OS updates for Windows and Linux servers)" + required: false + default: true + + arc_defender_enabled: + type: boolean + description: "Enable Microsoft Defender for Cloud integration (threat detection and vulnerability management)" + required: false + default: true + + arc_vm_insights_enabled: + type: boolean + description: "Enable VM Insights for performance monitoring (collects performance data and application dependencies)" + required: false + default: false + + arc_guest_configuration_enabled: + type: boolean + description: "Enable Azure Machine Configuration for guest configuration policies (audit settings inside Arc-enabled machines)" + required: false + default: false + +# ============================================================================= +# AZURE VIRTUAL DESKTOP +# ============================================================================= +# AVD configuration for Azure-hosted and Azure Local session hosts +# Reference: https://learn.microsoft.com/azure/virtual-desktop/ +# ============================================================================= + +avd: + azure: + infrastructure_type: ["avd_azure"] + # Host Pool Configuration + avd_host_pool_name: + type: string + description: "Name of AVD host pool" + required: false + pattern: "^[a-zA-Z0-9-_]{1,64}$" + example: "hp-avd-prod-pooled-eus" + + avd_host_pool_type: + type: string + description: "Host pool type (Pooled = multi-user shared, Personal = dedicated single-user)" + required: false + allowedValues: + - Pooled + - Personal + default: "Pooled" + reference: "https://learn.microsoft.com/azure/virtual-desktop/terminology" + + avd_host_pool_load_balancer: + type: string + description: "Load balancing algorithm for pooled host pools (BreadthFirst = distribute evenly, DepthFirst = fill one host before moving to next)" + required: false + allowedValues: + - BreadthFirst + - DepthFirst + default: "BreadthFirst" + applies_to: "Pooled host pools only" + + avd_max_session_limit: + type: integer + description: "Maximum concurrent sessions per session host (balance cost vs performance)" + required: false + minimum: 1 + maximum: 50 + default: 16 + applies_to: "Pooled host pools only" + + # Session Host Configuration + avd_session_host_count: + type: integer + description: "Number of session hosts to deploy" + required: false + minimum: 1 + maximum: 1000 + default: 2 + + avd_session_host_size: + type: string + description: "Azure VM SKU for session hosts" + required: false + default: "Standard_D4s_v5" + example: "Standard_D4s_v5" + note: "Common SKUs: D4s_v5 (4vCPU, 16GB), D8s_v5 (8vCPU, 32GB), D16s_v5 (16vCPU, 64GB)" + + avd_session_host_os: + type: string + description: "Operating system for session hosts (multi-session exclusive to AVD, reduces VM count)" + required: false + allowedValues: + - Windows-11-Enterprise-Multi-Session + - Windows-11-Enterprise + - Windows-10-Enterprise-Multi-Session + - Windows-10-Enterprise + - Windows-Server-2025 + - Windows-Server-2022 + - Windows-Server-2019 + default: "Windows-11-Enterprise-Multi-Session" + note: "Only 64-bit versions supported. Ultra disks NOT supported for OS disk." + reference: "https://learn.microsoft.com/azure/virtual-desktop/prerequisites" + + avd_session_host_disk_type: + type: string + description: "OS disk type for session hosts (Premium_LRS recommended for production)" + required: false + allowedValues: + - Standard_LRS + - StandardSSD_LRS + - Premium_LRS + default: "Premium_LRS" + + # FSLogix Profile Configuration + avd_fslogix_enabled: + type: boolean + description: "Enable FSLogix profile containers (required for pooled environments to persist user profiles)" + required: false + default: true + applies_to: "Pooled host pools only" + + avd_fslogix_storage_type: + type: string + description: "Storage backend for FSLogix profiles" + required: false + allowedValues: + - AzureFiles + - AzureNetAppFiles + default: "AzureFiles" + note: "Azure Files = SMB shares with AD DS or Entra ID auth, NetApp Files = high-performance NFS" + + avd_fslogix_storage_account: + type: string + description: "Azure Storage account name for FSLogix profiles" + required: false + pattern: "^[a-z0-9]{3,24}$" + example: "stavdprofileseus001" + + avd_fslogix_share_name: + type: string + description: "File share name for FSLogix profiles" + required: false + default: "profiles" + example: "avd-profiles" + + # Application Group & Workspace + avd_app_group_type: + type: string + description: "Application group type (Desktop = full desktop, RemoteApp = individual apps)" + required: false + allowedValues: + - Desktop + - RemoteApp + default: "Desktop" + note: "Multiple RemoteApp groups allowed per host pool" + reference: "https://learn.microsoft.com/azure/virtual-desktop/terminology#application-groups" + + avd_workspace_name: + type: string + description: "AVD workspace name (logical grouping of application groups)" + required: false + example: "ws-avd-prod-eus" + + # RDP Shortpath + avd_rdp_shortpath_enabled: + type: boolean + description: "Enable RDP Shortpath for direct UDP transport (provides lower latency, requires UDP port open)" + required: false + default: false + + azure_local: + infrastructure_type: ["avd_azure_local"] + # Prerequisites & Enablement + avd_azl_enabled: + type: boolean + description: "Enable Azure Virtual Desktop on Azure Local (requires 23H2+, cluster registered with Azure, AD DS domain)" + required: false + default: false + prerequisites: + - Azure Local version 23H2 or later + - Cluster registered with Azure + - Active Directory Domain Services (AD DS) domain + note: "Generally available (GA) feature. Requires Azure Local service fee + per-vCPU AVD fee." + reference: "https://learn.microsoft.com/azure/virtual-desktop/azure-stack-hci-overview" + + # Host Pool Configuration + avd_azl_host_pool_name: + type: string + description: "AVD host pool name for Azure Local session hosts" + required: false + example: "hp-avd-azl-prod-pooled" + critical_limitation: "Host pools CANNOT mix Azure and Azure Local session hosts. Must be 100% Azure or 100% Azure Local." + + avd_azl_session_host_count: + type: integer + description: "Number of AVD session hosts on Azure Local" + required: false + minimum: 1 + maximum: 100 + default: 2 + + avd_azl_session_host_os: + type: string + description: "Operating system for Azure Local session hosts (only 64-bit versions supported)" + required: false + allowedValues: + - Windows-11-Enterprise-Multi-Session + - Windows-11-Enterprise + - Windows-10-Enterprise-Multi-Session + - Windows-10-Enterprise + - Windows-Server-2025 + - Windows-Server-2022 + - Windows-Server-2019 + default: "Windows-11-Enterprise-Multi-Session" + + # Azure Connected Machine Agent (REQUIRED) + avd_azl_connected_machine_agent_required: + type: boolean + description: "Azure Connected Machine agent required for IMDS (auto-installed via Azure portal, enables Arc VM extensions)" + required: false + default: true + immutable: true + + # Storage Configuration + avd_azl_storage_path: + type: string + description: "CSV path for AVD session host VHDXs" + required: false + pattern: "^C:\\\\ClusterStorage\\\\Volume\\d+\\\\.+$" + example: "C:\\ClusterStorage\\Volume01\\AVD" + + avd_azl_profile_storage_type: + type: string + description: "FSLogix profile storage type for Azure Local" + required: false + allowedValues: + - StorageSpacesDirect + - LocalSMB + - AzureFiles + default: "StorageSpacesDirect" + note: "Microsoft recommends Storage Spaces Direct VM-based file share cluster for optimal performance" + reference: "https://learn.microsoft.com/azure/virtual-desktop/azure-stack-hci-overview#fslogix-profile-storage" + + # Licensing & Activation + avd_azl_licensing_method: + type: string + description: "OS licensing/activation method for Azure Local VMs" + required: false + allowedValues: + - AzureVerification + - TraditionalActivation + default: "AzureVerification" + note: "AzureVerification = Windows 11/10 multi-session, Server 2022 Datacenter: Azure Edition. TraditionalActivation = other OS images." + reference: "https://learn.microsoft.com/azure-stack/hci/deploy/azure-verification" + +# ============================================================================= +# KEY VAULT +# ============================================================================= + +keyvault: + # ============================================================================= + # CMP PLATFORM KEY VAULT (Management Tenant - Cross-Tenant Secrets) + # ============================================================================= + + cmp_platform: + kv_cmp_platform_name: + category: "security" + subcategory: "keyvault" + type: string + description: "CMP platform Key Vault name (management tenant - cross-tenant secrets)" + required: true + minLength: 3 + maxLength: 24 + pattern: "^[a-z][a-z0-9]+(-[a-z0-9]+)*$" + note: "No consecutive hyphens allowed" + example: "kv-azlc-mgmt" + + kv_cmp_platform_resource_group: + category: "security" + subcategory: "keyvault" + type: string + description: "CMP platform Key Vault resource group (function-based naming)" + required: true + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(identity|management)-[a-z0-9]+$" + example: "rg-azlc-iic-eus-identity-mgmt" + + kv_cmp_platform_location: + category: "security" + subcategory: "keyvault" + type: string + description: "CMP platform Key Vault Azure region" + required: true + allowedValues: + - eastus + - eastus2 + - westus + - westus2 + - centralus + example: "eastus" + + kv_cmp_platform_subscription_id: + type: string + description: "Management tenant subscription ID for CMP platform Key Vault" + required: true + format: uuid + example: "00000000-1111-2222-3333-444444444444" + + kv_cmp_platform_tenant_id: + type: string + description: "Management tenant ID (azurelocalcloud)" + required: true + format: uuid + example: "00000000-aaaa-bbbb-cccc-111111111111" + + kv_cmp_platform_sku: + type: string + description: "CMP platform Key Vault SKU" + required: false + allowedValues: + - standard + - premium + default: "standard" + + kv_cmp_platform_enable_rbac: + type: boolean + description: "Enable RBAC authorization" + required: false + default: true + + kv_cmp_platform_enable_soft_delete: + type: boolean + description: "Enable soft delete" + required: false + default: true + + kv_cmp_platform_soft_delete_retention_days: + type: integer + description: "Soft delete retention days" + required: false + minimum: 7 + maximum: 90 + default: 90 + + kv_cmp_platform_enable_purge_protection: + type: boolean + description: "Enable purge protection" + required: false + default: true + + kv_cmp_platform_network_acls_default_action: + type: string + description: "Network ACLs default action" + required: false + allowedValues: + - Allow + - Deny + default: "Deny" + + kv_cmp_platform_private_endpoint_enabled: + type: boolean + description: "Enable private endpoint" + required: false + default: true + + kv_cmp_platform_private_endpoint_subnet_id: + type: string + description: "Private endpoint subnet resource ID" + required: false + example: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-connectivity-hub/providers/Microsoft.Network/virtualNetworks/vnet-azlc-iic-eus-connectivity-hub/subnets/snet-azlc-iic-eus-connectivity-pep" + + kv_cmp_platform_diagnostics_enabled: + type: boolean + description: "Enable diagnostic settings" + required: false + default: true + + kv_cmp_platform_diagnostics_workspace_id: + type: string + description: "Log Analytics workspace resource ID" + required: false + example: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-management-monitoring/providers/Microsoft.OperationalInsights/workspaces/log-azlc-iic-eus" + + kv_cmp_platform_logs_retention_days: + type: integer + description: "Diagnostic logs retention days" + required: false + minimum: 0 + maximum: 365 + default: 90 + + # ============================================================================= + # PLATFORM KEY VAULT (Per-Tenant Secrets) + # ============================================================================= + + platform: + kv_platform_name: + type: string + description: "Platform Key Vault name (tenant-specific secrets)" + required: true + minLength: 3 + maxLength: 24 + pattern: "^[a-z][a-z0-9]+(-[a-z0-9]+)*$" + note: "No consecutive hyphens allowed" + example: "kv-iic-platform" + + kv_platform_resource_group: + type: string + description: "Platform Key Vault resource group (function-based naming)" + required: true + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(identity|management)-[a-z0-9]+$" + example: "rg-iic-iic-eus-identity-mgmt" + + kv_platform_location: + type: string + description: "Platform Key Vault Azure region" + required: true + allowedValues: + - eastus + - eastus2 + - westus + - westus2 + - centralus + example: "eastus" + + kv_platform_subscription_id: + type: string + description: "Tenant subscription ID" + required: true + format: uuid + example: "12345678-1234-1234-1234-123456789012" + + kv_platform_tenant_id: + type: string + description: "Tenant ID" + required: true + format: uuid + example: "87654321-4321-4321-4321-210987654321" + + kv_platform_sku: + type: string + description: "Platform Key Vault SKU" + required: false + allowedValues: + - standard + - premium + default: "standard" + + kv_platform_enable_rbac: + type: boolean + description: "Enable RBAC authorization" + required: false + default: true + + kv_platform_enable_soft_delete: + type: boolean + description: "Enable soft delete" + required: false + default: true + + kv_platform_soft_delete_retention_days: + type: integer + description: "Soft delete retention days" + required: false + minimum: 7 + maximum: 90 + default: 90 + + kv_platform_enable_purge_protection: + type: boolean + description: "Enable purge protection" + required: false + default: true + + kv_platform_network_acls_default_action: + type: string + description: "Network ACLs default action" + required: false + allowedValues: + - Allow + - Deny + default: "Deny" + + kv_platform_private_endpoint_enabled: + type: boolean + description: "Enable private endpoint" + required: false + default: true + + kv_platform_private_endpoint_subnet_id: + type: string + description: "Private endpoint subnet resource ID" + required: false + example: "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/rg-iic-iic-eus-connectivity-hub/providers/Microsoft.Network/virtualNetworks/vnet-iic-iic-eus-connectivity-hub/subnets/snet-iic-iic-eus-connectivity-pep" + + kv_platform_diagnostics_enabled: + type: boolean + description: "Enable diagnostic settings" + required: false + default: true + + kv_platform_diagnostics_workspace_id: + type: string + description: "Log Analytics workspace resource ID" + required: false + example: "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/rg-iic-iic-eus-management-monitoring/providers/Microsoft.OperationalInsights/workspaces/log-iic-iic-eus" + + kv_platform_logs_retention_days: + type: integer + description: "Diagnostic logs retention days" + required: false + minimum: 0 + maximum: 365 + default: 90 + + # ============================================================================= + # AZURE LOCAL DEPLOYMENT KEY VAULT (Cluster Deployment) + # ============================================================================= + + kv_azl: + kv_azl_name: + type: string + description: "Azure Local deployment Key Vault name (created during cluster deployment)" + required: true + minLength: 3 + maxLength: 24 + pattern: "^[a-z][a-z0-9]+(-[a-z0-9]+)*$" + note: "No consecutive hyphens allowed. Typically auto-generated by deployment." + example: "kv-azlc-clus01" + + kv_azl_resource_group: + type: string + description: "Azure Local deployment Key Vault resource group (function-based naming)" + required: true + pattern: "^rg-[a-z0-9]+-(iic|demo|poc|lab)-[a-z0-9]+-(landingzone|management)-[a-z0-9]+$" + example: "rg-azlc-iic-eus-landingzone-cluster" + + kv_azl_location: + type: string + description: "Azure Local deployment Key Vault Azure region (must match cluster region)" + required: true + allowedValues: + - eastus + - eastus2 + - westus + - westus2 + - centralus + example: "eastus" + + kv_azl_subscription_id: + type: string + description: "Subscription ID for Azure Local cluster" + required: true + format: uuid + example: "00000000-1111-2222-3333-444444444444" + + kv_azl_tenant_id: + type: string + description: "Tenant ID for Azure Local cluster" + required: true + format: uuid + example: "00000000-aaaa-bbbb-cccc-111111111111" + + kv_azl_sku: + type: string + description: "Azure Local deployment Key Vault SKU" + required: false + allowedValues: + - standard + - premium + default: "standard" + note: "Standard is sufficient for cluster secrets" + + kv_azl_enable_rbac: + type: boolean + description: "Enable RBAC authorization" + required: false + default: true + + kv_azl_enable_soft_delete: + type: boolean + description: "Enable soft delete" + required: false + default: true + + kv_azl_soft_delete_retention_days: + type: integer + description: "Soft delete retention days" + required: false + minimum: 7 + maximum: 90 + default: 90 + + kv_azl_enable_purge_protection: + type: boolean + description: "Enable purge protection" + required: false + default: true + + kv_azl_local_admin_username: + type: string + description: "Local administrator username" + required: true + example: "azureadmin" + + kv_azl_local_admin_password: + type: string + description: "Local administrator password (Key Vault secret name)" + required: true + sensitive: true + example: "LocalAdminPassword" + + kv_azl_ad_domain_admin_username: + type: string + description: "Active Directory domain administrator username" + required: false + example: "domainadmin@azlocal.mgmt" + + kv_azl_ad_domain_admin_password: + type: string + description: "Active Directory domain administrator password (Key Vault secret name)" + required: false + sensitive: true + example: "DomainAdminPassword" + + kv_azl_arbdeployment_spn_value: + type: string + description: "Azure Resource Bridge deployment SPN secret (Key Vault secret name)" + required: true + sensitive: true + example: "ArbDeploymentSPNValue" + + kv_azl_storage_witness_account_key: + type: string + description: "Storage account key for cloud witness (Key Vault secret name)" + required: false + sensitive: true + example: "WitnessStorageKey" + + kv_azl_default_arc_gateway_resource_id: + type: string + description: "Default Azure Arc gateway resource ID (Key Vault secret name)" + required: false + example: "DefaultArcGatewayResourceId" + + kv_azl_network_acls_default_action: + type: string + description: "Network ACLs default action" + required: false + allowedValues: + - Allow + - Deny + default: "Allow" + note: "Typically set to Allow for deployment process access" + + kv_azl_private_endpoint_enabled: + type: boolean + description: "Enable private endpoint (not recommended during deployment)" + required: false + default: false + note: "Private endpoint can be enabled post-deployment" + + kv_azl_diagnostics_enabled: + type: boolean + description: "Enable diagnostic settings" + required: false + default: true + + kv_azl_diagnostics_workspace_id: + type: string + description: "Log Analytics workspace resource ID" + required: false + example: "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-azlc-iic-eus-management-monitoring/providers/Microsoft.OperationalInsights/workspaces/log-azlc-iic-eus" + + kv_azl_logs_retention_days: + type: integer + description: "Diagnostic logs retention days" + required: false + minimum: 0 + maximum: 365 + default: 90 + +# ============================================================================= +# STORAGE +# ============================================================================= + +storage: + storage_name: + type: string + description: "Storage account name" + required: false + minLength: 3 + maxLength: 24 + pattern: "^[a-z0-9]+$" + example: "stazlciiceus001" + + storage_tier: + type: string + description: "Storage account tier" + required: false + allowedValues: + - Standard + - Premium + default: "Standard" + + storage_replication_type: + type: string + description: "Storage replication type" + required: false + allowedValues: + - LRS + - GRS + - RAGRS + - ZRS + - GZRS + - RAGZRS + default: "LRS" + + storage_kind: + type: string + description: "Storage account kind" + required: false + allowedValues: + - Storage + - StorageV2 + - BlobStorage + - FileStorage + - BlockBlobStorage + default: "StorageV2" + + storage_container_name: + category: "storage" + subcategory: "account" + type: string + description: "Storage container name" + required: false + pattern: "^[a-z0-9-]+$" + example: "terraform-state" + + storage_enable_https_only: + type: boolean + description: "Enable HTTPS traffic only" + required: false + default: true + + # Monitoring Storage Account + storage_monitoring_name: + category: "storage" + subcategory: "monitoring" + type: string + description: "Monitoring storage account name" + required: false + minLength: 3 + maxLength: 24 + pattern: "^[a-z0-9]+$" + example: "stazlciiceusmon001" + + monitoring_storage_resource_group: + type: string + description: "Monitoring storage resource group" + required: false + pattern: "^rg-[a-z0-9-]+$" + example: "rg-azlc-iic-eus-management-monitoring" + + monitoring_storage_sku: + type: string + description: "Monitoring storage SKU (combines tier and replication)" + required: false + allowedValues: + - Standard_LRS + - Standard_GRS + - Standard_RAGRS + - Standard_ZRS + - Premium_LRS + default: "Standard_LRS" + + monitoring_storage_tier: + type: string + description: "Monitoring storage access tier" + required: false + allowedValues: + - Hot + - Cool + default: "Hot" + + storage_min_tls_version: + type: string + description: "Minimum TLS version" + required: false + allowedValues: + - TLS1_0 + - TLS1_1 + - TLS1_2 + default: "TLS1_2" + + # Cloud Witness Storage Account + storage_witness_name: + type: string + description: "Cloud witness storage account name for cluster quorum" + required: false + minLength: 3 + maxLength: 24 + pattern: "^[a-z0-9]+$" + example: "stazlciiceuswitness" + + witness_storage_resource_group: + type: string + description: "Resource group for witness storage account" + required: false + pattern: "^rg-[a-z0-9-]+$" + example: "rg-azlc-iic-eus-management-witness" + + witness_storage_sku: + type: string + description: "Cloud witness storage account SKU" + required: false + allowedValues: + - Standard_LRS + - Standard_GRS + - Standard_RAGRS + default: "Standard_LRS" + + witness_storage_tier: + type: string + description: "Cloud witness storage account access tier" + required: false + allowedValues: + - Hot + - Cool + default: "Hot" + + # Diagnostics Storage Account + storage_diagnostics_name: + type: string + description: "Diagnostics storage account name for logs and metrics" + required: false + minLength: 3 + maxLength: 24 + pattern: "^[a-z0-9]+$" + example: "stazlciiceusdiag" + + diagnostics_storage_resource_group: + type: string + description: "Resource group for diagnostics storage account" + required: false + pattern: "^rg-[a-z0-9-]+$" + example: "rg-azlc-iic-eus-management-monitoring" + + diagnostics_storage_sku: + type: string + description: "Diagnostics storage account SKU" + required: false + allowedValues: + - Standard_LRS + - Standard_GRS + - Standard_RAGRS + default: "Standard_LRS" + + # Network Flow Logs Storage Account + storage_flowlogs_name: + type: string + description: "Storage account for Network Watcher and VNet flow logs" + required: false + minLength: 3 + maxLength: 24 + pattern: "^[a-z0-9]+$" + example: "stazlcprodteceusflowlogs" + + flowlogs_storage_resource_group: + type: string + description: "Resource group for flow logs storage account" + required: false + pattern: "^rg-[a-z0-9-]+$" + example: "rg-azlc-iic-eus-connectivity-hub" + + flowlogs_storage_sku: + type: string + description: "Flow logs storage account SKU" + required: false + allowedValues: + - Standard_LRS + - Standard_GRS + - Standard_RAGRS + - Standard_ZRS + default: "Standard_LRS" + + # Bastion Storage Account + storage_bastion_name: + type: string + description: "Storage account for Bastion session recordings and diagnostics" + required: false + minLength: 3 + maxLength: 24 + pattern: "^[a-z0-9]+$" + example: "stazlciiceusbastion" + + bastion_storage_resource_group: + type: string + description: "Resource group for Bastion storage account" + required: false + pattern: "^rg-[a-z0-9-]+$" + example: "rg-azlc-iic-eus-connectivity-hub" + + bastion_storage_sku: + type: string + description: "Bastion storage account SKU" + required: false + allowedValues: + - Standard_LRS + - Standard_GRS + - Standard_RAGRS + default: "Standard_LRS" + + # Firewall Storage Account + fw_storage_account_name: + type: string + description: "Storage account for firewall diagnostic logs" + required: false + minLength: 3 + maxLength: 24 + pattern: "^[a-z0-9]+$" + example: "stazlcprodteceusfw" + + fw_storage_resource_group: + type: string + description: "Resource group for firewall storage account" + required: false + pattern: "^rg-[a-z0-9-]+$" + example: "rg-azlc-iic-eus-connectivity-hub" + + fw_storage_sku: + type: string + description: "Firewall storage account SKU" + required: false + allowedValues: + - Standard_LRS + - Standard_GRS + - Standard_RAGRS + default: "Standard_LRS" + + # VM Boot Diagnostics Storage + storage_vm_boot_diagnostics_uri: + type: string + description: "Storage account URI for VM boot diagnostics (leave empty for managed storage)" + required: false + example: "https://stazlcprodteceusdiag.blob.core.windows.net/" + +# ============================================================================= +# MICROSOFT DEFENDER FOR CLOUD & SECURITY +# ============================================================================= + +defender: + # Core Defender for Cloud Settings + def_for_cloud_enabled: + type: boolean + description: "Microsoft Defender for Cloud enabled" + required: false + default: true + + def_for_servers_enabled: + type: boolean + description: "Defender for Servers enabled" + required: false + default: true + + def_for_storage_enabled: + type: boolean + description: "Defender for Storage enabled" + required: false + default: true + + # Security & Compliance + def_security_contact_email: + type: string + description: "Security contact email" + required: false + format: email + example: "security@improbability.cloud" + + def_compliance_framework: + type: string + description: "Compliance framework" + required: false + allowedValues: + - NIST + - CIS + - PCI-DSS + - HIPAA + - None + default: "CIS" + + # Alert Configuration + def_alert_email_recipients: + type: array + description: "Defender alert email recipients" + required: true + items: + type: string + format: email + example: + - "security@azlocal.cloud" + - "admin@azlocal.cloud" + - "operations@azlocal.cloud" + + def_alert_notifications: + type: boolean + description: "Defender alert notifications" + required: true + example: "true" + + def_alert_severity_threshold: + type: string + description: "Defender alert severity threshold" + required: true + example: "Medium" + + def_alerts_to_admins: + type: boolean + description: "Defender alerts to admins" + required: true + example: "true" + + def_auto_provisioning_ama: + type: boolean + description: "Defender auto provisioning AMA" + required: true + example: "true" + + def_auto_provisioning_endpoint_protection: + type: boolean + description: "Defender auto provisioning endpoint protection" + required: true + example: "true" + + def_auto_provisioning_vulnerability: + type: boolean + description: "Defender auto provisioning vulnerability" + required: true + example: "true" + + def_data_ingestion_benefit_enabled: + type: boolean + description: "Defender data ingestion benefit enabled" + required: true + example: "true" + + def_dns_enabled: + type: boolean + description: "Defender for DNS enabled" + required: true + example: "true" + + def_endpoint_integration_enabled: + type: boolean + description: "Defender endpoint integration enabled" + required: true + example: "true" + + def_endpoint_unified_solution: + type: boolean + description: "Defender endpoint unified solution" + required: true + example: "true" + + def_file_integrity_monitoring: + type: boolean + description: "Defender file integrity monitoring" + required: true + example: "true" + + def_foundational_cspm_enabled: + type: boolean + description: "Defender foundational CSPM enabled" + required: true + example: "true" + + def_jit_vm_access: + type: boolean + description: "Defender JIT VM access" + required: true + example: "true" + + def_key_vault_enabled: + type: boolean + description: "Defender for key vault enabled" + required: true + example: "true" + + def_log_analytics_workspace_id: + type: string + description: "Defender log analytics workspace ID" + required: true + example: "/subscriptions/.../workspaces/law-azlc-iic-eus" + + def_log_retention_days: + type: integer + description: "Defender log retention days" + required: true + example: "90" + + def_network_map_enabled: + type: boolean + description: "Defender network map enabled" + required: true + example: "true" + + def_resource_manager_enabled: + type: boolean + description: "Defender for resource manager enabled" + required: true + example: "true" + + def_security_alerts_enabled: + type: boolean + description: "Defender security alerts enabled" + required: true + example: "true" + + def_servers_enabled: + type: boolean + description: "Defender for servers enabled" + required: true + example: "true" + + def_servers_scope: + type: string + description: "Defender for servers scope" + required: true + example: "Subscription" + + def_servers_tier: + type: string + description: "Defender for servers tier" + required: true + example: "Standard" + + def_storage_enabled: + type: boolean + description: "Defender for storage enabled" + required: true + example: "true" + + def_vulnerability_solution: + type: boolean + description: "Defender vulnerability solution" + required: true + example: "true" + +# ============================================================================= +# B2B CROSS-TENANT SYNCHRONIZATION +# ============================================================================= +# Cross-tenant synchronization (CTS) configuration for IIC managed services. +# Enables IIC engineers to access customer Azure tenants via B2B collaboration. +# +# RELATED SECTIONS: +# - azure_tenants: (line 390+) - Tenant ID definitions +# - accounts: Service principal and user accounts +# - active_directory: AD integration for hybrid identity +# +# USAGE: +# - Stage 00 B2B Configuration: Configure CTS between IIC MGMT and customer tenant +# - Scripts: 02-iic-cicd-infra/powershell/Configure-*.ps1 + +b2b_configuration: + # --------------------------------------------------------------------------- + # TIERPOINT MANAGEMENT TENANT (Source Tenant) + # --------------------------------------------------------------------------- + iic_mgmt: + b2b_iic_mgmt_tenant_id: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "IIC MGMT tenant ID (source tenant for cross-tenant sync)" + required: true + format: uuid + example: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + + b2b_iic_mgmt_display_name: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "IIC MGMT tenant display name" + required: true + example: "IIC MGMT" + + b2b_iic_mgmt_primary_domain: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "IIC MGMT tenant primary domain" + required: true + pattern: "^[a-z0-9-]+\\.onmicrosoft\\.com$" + example: "iic-mgmt.onmicrosoft.com" + + # --------------------------------------------------------------------------- + # TARGET TENANT (Customer/Destination Tenant) + # --------------------------------------------------------------------------- + target_tenant: + b2b_target_tenant_id: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "Target (customer) tenant ID for cross-tenant sync" + required: true + format: uuid + example: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + + b2b_target_display_name: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "Target tenant display name" + required: true + example: "Customer Production" + + b2b_target_primary_domain: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "Target tenant primary domain" + required: true + pattern: "^[a-z0-9-]+\\.onmicrosoft\\.com$" + example: "customer.onmicrosoft.com" + + # --------------------------------------------------------------------------- + # SERVICE TIER CONFIGURATION + # --------------------------------------------------------------------------- + service_configuration: + b2b_service_tier: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "Service tier purchased by customer (determines which groups are synced)" + required: true + allowedValues: + - Professional + - Premium + default: "Professional" + example: "Professional" + + b2b_additional_services: + category: "identity" + subcategory: "b2b-configuration" + type: array + description: "Additional services purchased beyond base tier" + required: false + items: + type: string + allowedValues: + - SOC + - AES + example: ["SOC"] + + # --------------------------------------------------------------------------- + # CROSS-TENANT SYNC TEMPLATE + # --------------------------------------------------------------------------- + cts_configuration: + b2b_cts_template_id: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "Cross-tenant synchronization template ID (Azure2Azure template)" + required: true + format: uuid + default: "518e5f48-1fc8-4c48-9387-9fdf28b0dfe7" + example: "518e5f48-1fc8-4c48-9387-9fdf28b0dfe7" + note: "Default Azure2Azure CTS template ID from Microsoft" + + # --------------------------------------------------------------------------- + # SERVICE TIER GROUPS (Source Groups in IIC MGMT) + # --------------------------------------------------------------------------- + service_tier_groups: + b2b_source_groups_professional: + category: "identity" + subcategory: "b2b-configuration" + type: array + description: "IIC MGMT groups synced for Professional tier" + required: false + items: + type: string + example: + - "Public Cloud Services" + - "Public Cloud Support" + - "Public Cloud Implementation Engineering" + + b2b_source_groups_premium: + category: "identity" + subcategory: "b2b-configuration" + type: array + description: "IIC MGMT groups synced for Premium tier (includes Professional + Premium)" + required: false + items: + type: string + example: + - "Public Cloud Services" + - "Public Cloud Support" + - "Public Cloud Implementation Engineering" + - "Public Cloud Premium" + + b2b_source_groups_admins: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "IIC tenant group for admin-level sync (highest access)" + required: false + example: "TRP-ENG-PCS-L3" + + b2b_source_groups_contributors: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "IIC tenant group for contributor-level sync" + required: false + example: "TRP-ENG-PCS-L2" + + b2b_source_groups_readers: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "IIC tenant group for reader-level sync" + required: false + example: "TRP-ENG-PCS" + + b2b_source_groups_premium_team: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "IIC tenant group for premium services team sync" + required: false + example: "TRP-ENG-PCS-PREMIUM" + note: "Single group reference - see b2b_source_groups_premium for array of Premium tier groups" + + # --------------------------------------------------------------------------- + # ADDITIONAL SERVICE GROUPS + # --------------------------------------------------------------------------- + additional_service_groups: + b2b_additional_service_groups_soc: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "IIC group for SOC services" + required: false + example: "SOC" + + b2b_additional_service_groups_aes: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "IIC group for Advanced Engineering Services" + required: false + example: "SEC-SD Advanced Services" + + # --------------------------------------------------------------------------- + # CUSTOMER TENANT GROUPS (Target Groups) + # --------------------------------------------------------------------------- + customer_groups: + b2b_bootstrap_group_name: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "Customer tenant bootstrap group (standard security group)" + required: false + default: "iic" + example: "iic" + note: "Initial group for IIC access before tiered groups are created" + + b2b_tiered_groups_admins: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "Customer tenant admin group (role-assignable)" + required: false + example: "tp-b2b-env-admins" + note: "Created with IsAssignableToRole:true for Entra directory role assignment" + + b2b_tiered_groups_contributors: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "Customer tenant contributor group (role-assignable)" + required: false + example: "tp-b2b-env-contributors" + + b2b_tiered_groups_readers: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "Customer tenant reader group (role-assignable)" + required: false + example: "tp-b2b-env-readers" + + # --------------------------------------------------------------------------- + # SERVICE PRINCIPAL CONFIGURATION + # --------------------------------------------------------------------------- + service_principal: + b2b_service_principal_name: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "Service principal name for CI/CD automation in customer tenant" + required: false + example: "iic-cicd" + + b2b_source_domain: + category: "identity" + subcategory: "b2b-configuration" + type: string + description: "Domain pattern for synced users (appears in UPN suffix)" + required: false + example: "azurelocalcloud.com" + + # --------------------------------------------------------------------------- + # TRUST SETTINGS + # --------------------------------------------------------------------------- + trust_settings: + b2b_trust_accept_mfa: + category: "identity" + subcategory: "b2b-configuration" + type: boolean + description: "Accept MFA claims from source tenant" + required: false + default: true + + b2b_trust_accept_compliant_devices: + category: "identity" + subcategory: "b2b-configuration" + type: boolean + description: "Accept compliant device claims from source tenant" + required: false + default: true + + b2b_trust_accept_hybrid_joined_devices: + category: "identity" + subcategory: "b2b-configuration" + type: boolean + description: "Accept hybrid Azure AD joined device claims from source tenant" + required: false + default: true + + b2b_trust_auto_redeem_invitations: + category: "identity" + subcategory: "b2b-configuration" + type: boolean + description: "Automatically redeem invitations for seamless access" + required: false + default: true + +# ============================================================================= +# GITLAB CI/CD CONFIGURATION +# ============================================================================= +# GitLab CI/CD configuration for automated deployments. +# +# RELATED SECTIONS: +# - service_principals: Azure service principal for GitLab runners +# - keyvault: Secret storage for GitLab variables + +gitlab: + gitlab_project_id: + category: "automation" + subcategory: "gitlab" + type: integer + description: "GitLab project ID for the deployment repository" + required: false + example: 12345 + + gitlab_project_path: + category: "automation" + subcategory: "gitlab" + type: string + description: "GitLab project path (group/project)" + required: false + example: "iic/azl-deployments" + + gitlab_runner_token: + category: "automation" + subcategory: "gitlab" + type: string + description: "GitLab runner registration token (Key Vault reference)" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-iic-secrets/gitlab-runner-token" + + gitlab_environment_name: + category: "automation" + subcategory: "gitlab" + type: string + description: "GitLab environment name for deployments" + required: false + example: "production" + +# ============================================================================= +# NETWORK CONFIGURATION (ON-PREM) +# ============================================================================= +# Additional network configuration variables discovered from scripts. + +network_config: + management_nic_name: + category: "networking" + subcategory: "adapters" + type: string + description: "Management network adapter name (varies by Dell platform)" + required: false + example: "Embedded NIC 1" + note: "Adapter name varies: 'Embedded NIC 1' for Intel onboard, 'Slot X Port Y' for Mellanox PCIe" + + jumbo_frames_mtu: + category: "networking" + subcategory: "adapters" + type: integer + description: "Jumbo frames MTU value for storage and RDMA traffic" + required: false + minimum: 1500 + maximum: 9014 + default: 9014 + example: 9014 + +# ============================================================================= +# CLUSTER NETWORKING (ADDITIONAL) +# ============================================================================= +# Additional cluster networking variables for IP pool management. + +cluster_networking: + cluster_ip_pool_start: + category: "networking" + subcategory: "cluster-ip-pools" + type: string + description: "Start of static IP pool for cluster resources" + required: false + format: ipv4 + example: "192.168.211.100" + + cluster_ip_pool_end: + category: "networking" + subcategory: "cluster-ip-pools" + type: string + description: "End of static IP pool for cluster resources" + required: false + format: ipv4 + example: "192.168.211.150" + +# ============================================================================= +# CLUSTER CONFIGURATION (ADDITIONAL) +# ============================================================================= +# Additional cluster configuration variables. + +cluster_config: + cloud_witness_storage_account: + category: "storage" + subcategory: "cluster-quorum" + type: string + description: "Cloud witness storage account name for cluster quorum" + required: false + minLength: 3 + maxLength: 24 + pattern: "^[a-z0-9]+$" + example: "stazlciiceuswitness" + +# ============================================================================= +# MONITORING (ADDITIONAL) +# ============================================================================= +# Additional monitoring configuration variables discovered from scripts. + +monitoring_extended: + datadog: + monitoring_datadog_api_key: + category: "monitoring" + subcategory: "datadog" + type: string + description: "Datadog API key for agent configuration" + required: false + sensitive: true + pattern: "^keyvault://[a-z0-9-]+/.+$" + example: "keyvault://kv-azlc-iic-secrets/datadog-api-key" + + monitoring_datadog_site: + category: "monitoring" + subcategory: "datadog" + type: string + description: "Datadog site URL (region-specific)" + required: false + allowedValues: + - "datadoghq.com" + - "us3.datadoghq.com" + - "us5.datadoghq.com" + - "datadoghq.eu" + default: "datadoghq.com" + example: "datadoghq.com" + + scom: + monitoring_scom_server: + category: "monitoring" + subcategory: "scom" + type: string + description: "SCOM management server FQDN" + required: false + example: "scom-mgmt01.azlocal.cloud" + + wac_monitoring: + monitoring_wac_server: + category: "monitoring" + subcategory: "wac" + type: string + description: "Windows Admin Center server FQDN for monitoring integration" + required: false + example: "wac01.azlocal.cloud" + note: "For WAC VM configuration, see azure_jumpbox_vm.wac section" + +# END OF REGISTRY +# ============================================================================= diff --git a/standards/variable-management/examples.mdx b/standards/variable-management/examples.mdx new file mode 100644 index 000000000..dfb691a9f --- /dev/null +++ b/standards/variable-management/examples.mdx @@ -0,0 +1,984 @@ +--- +id: examples +title: Usage Examples +sidebar_label: Examples +--- + +# Usage Examples + +[![Examples](https://img.shields.io/badge/Type-Examples-blue?logo=github)](https://github.com/features/examples) +[![Practical](https://img.shields.io/badge/Level-Practical-green)](https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/) +[![Implementation](https://img.shields.io/badge/Implementation-Real_World-purple)](https://docs.microsoft.com/en-us/powershell/) + +> **Real-world examples of variable registry implementation across different scenarios and environments** + +**Example Library** | **Implementation Patterns** | **Real-World Scenarios** + +--- + +## Overview + +This document provides comprehensive examples of how to implement the variable registry system for customer deployments. Each example includes complete configuration files, scripts, and validation outputs. + +> **Note:** Real configurations for each customer environment live in their respective repository's `configs/infrastructure.yml`. The examples below use a fictional company — **Infinite Improbability Corp** — to demonstrate the full configuration structure. + +### Example Categories + +- **Environment Configurations** - Complete infrastructure.yml example +- **Script Integration** - PowerShell, Python, and Bash examples +- **IaC Templates** - Bicep, Terraform, ARM examples +- **CI/CD Pipelines** - GitHub Actions, GitLab CI, Azure DevOps +- **Validation Scenarios** - Error examples and fixes +- **Migration Examples** - Converting legacy configurations + +--- + +## Environment Configuration Example + +### Infinite Improbability Corp (4-Node Production Cluster) + +> **Example Company:** Infinite Improbability Corp (`improbability.cloud`) is a fictional company used throughout this documentation. Prefix: `iic`, NetBIOS: `IMPROBABLE`, cluster: `iic-clus01`, nodes: `iic-01-n01` through `iic-01-n04`. + +**Characteristics:** +- 4-node production cluster +- Traditional switched network topology +- Full datacenter integration with Dell AX hardware +- GPU-enabled for AI/ML workloads + +```yaml +# infrastructure.yml - Infinite Improbability Corp +version: "1.0.0" +lastUpdated: "2026-02-01" + +# Site Information +site: + code: "IIC" + name: "Infinite Improbability Corp" + location: "Sector ZZ9 Plural Z Alpha" + environment: "Production" + owner: "platform-engineering@improbability.cloud" + +# ============================================================================= +# 6. AZURE PLATFORM (from registry: azure_platform) +# ============================================================================= +azure_platform: + tenant: + id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + name: "Infinite Improbability Corp" + default_domain: "improbability.cloud" + subscriptions: + production: + id: "f9e8d7c6-b5a4-3210-fedc-ba9876543210" + name: "iic-production-001" + purpose: "Production Azure Local cluster and Arc resources" + region: "eastus" + resource_group_name: "rg-iic-azl-eus-cluster-01" + +# ============================================================================= +# 5. TAGS +# ============================================================================= +tags: + Environment: "Production" + Project: "Infinite Improbability Corp" + Owner: "platform-engineering@improbability.cloud" + ManagedBy: "Infinite Improbability Corp" + CostCenter: "iic-42" + +# ============================================================================= +# 7. IDENTITY (from registry: identity) +# ============================================================================= +identity: + active_directory: + ad_enabled: true + ad_domain_fqdn: "improbability.cloud" + ad_netbios_name: "IMPROBABLE" + ad_computers_ou_path: "OU=AzureLocal,DC=improbability,DC=cloud" + accounts: + account_local_admin_username: "localadmin" + account_local_admin_password: "keyvault://kv-iic-platform/local-admin-password" + account_ad_domain_admin_username: "iicadmin@improbability.cloud" + account_ad_domain_admin_password: "keyvault://kv-iic-platform/domain-admin-password" + +# ============================================================================= +# 8. NETWORKING (from registry: networking) +# ============================================================================= +networking: + onprem: + network_intents: + - name: "ManagementCompute" + traffic_types: [Management, Compute] + adapter_names: ["SLOT 3 Port 1", "SLOT 3 Port 2"] + subnet: "10.42.1.0/24" + gateway: "10.42.1.1" + vlan_id: 101 + dns_servers: ["10.42.1.10", "10.42.1.11"] + - name: "Storage" + traffic_types: [Storage] + adapter_names: ["SLOT 6 Port 1", "SLOT 6 Port 2"] + storage_networks: + - subnet: "10.71.1.0/24" + vlan_id: 711 + - subnet: "10.71.2.0/24" + vlan_id: 712 + vlans: + oob_management: + vlan_id: 110 + subnet: "10.42.10.0/24" + gateway: "10.42.10.1" + +# ============================================================================= +# 9. COMPUTE (from registry: compute) +# ============================================================================= +compute: + azure_local: + cluster_name: "iic-clus01" + cluster_node_count: 4 + arc_resource_group: "rg-iic-azl-eus-cluster-01" + network_topology: "switched" + identity_provider: "active_directory" + witness_type: "cloud_witness" + deployment_mode: "validate_and_deploy" + cluster_nodes: + node01: + hostname: "iic-01-n01" + management_ip: "10.42.1.11" + idrac_ip: "10.42.10.11" + service_tag: "IIC0001" + rack_position: 10 + node02: + hostname: "iic-01-n02" + management_ip: "10.42.1.12" + idrac_ip: "10.42.10.12" + service_tag: "IIC0002" + rack_position: 11 + node03: + hostname: "iic-01-n03" + management_ip: "10.42.1.13" + idrac_ip: "10.42.10.13" + service_tag: "IIC0003" + rack_position: 12 + node04: + hostname: "iic-01-n04" + management_ip: "10.42.1.14" + idrac_ip: "10.42.10.14" + service_tag: "IIC0004" + rack_position: 13 + +# ============================================================================= +# 11. SECURITY (from registry: security) +# ============================================================================= +security: + keyvault: + platform: + kv_platform_name: "kv-iic-platform" + kv_platform_resource_group: "rg-iic-mgmt-eus-01" + kv_platform_location: "eastus" + kv_platform_sku: "premium" + kv_platform_enable_rbac: true + kv_platform_enable_soft_delete: true + kv_platform_soft_delete_retention_days: 90 + kv_platform_enable_purge_protection: true + +# Hardware Configuration (from registry: site.hardware) +site: + hardware: + vendor: "Dell" + model: "AX-760" + gpu_enabled: true + gpu_model: "NVIDIA-L4" + gpu_count: 1 + +# Entra ID Security Groups (from registry: identity) +identity: + entra_id: + security_groups: + admins: + name: "grp-iic-admins" + purpose: "Environment administrators" + contributors: + name: "grp-iic-contributors" + purpose: "Environment contributors" + readers: + name: "grp-iic-readers" + purpose: "Environment readers" +``` + +--- + +## Script Integration Examples + +### PowerShell Deployment Script + +```powershell +<# +.SYNOPSIS + Deploy Azure Local cluster using registry variables. + +.DESCRIPTION + This script demonstrates how to use the variable registry in PowerShell scripts. + It loads infrastructure.yml and accesses variables defined in the master registry. + + Variables used from master registry: + - compute.azure_local.cluster_name (string, required, maxLength: 15) + - compute.azure_local.cluster_node_count (integer, required, 1-16) + - compute.cluster_nodes (object, required) + - networking.onprem.network_intents (array, required) + - identity.accounts.account_local_admin_* (credentials) + +.PARAMETER ConfigPath + Path to infrastructure.yml file containing cluster configuration. + File must conform to infrastructure.schema.json validation. + +.PARAMETER DryRun + Perform validation only, do not execute deployment. + +.EXAMPLE + .\Deploy-AzureLocal.ps1 -ConfigPath .\infrastructure.yml + +.EXAMPLE + .\Deploy-AzureLocal.ps1 -ConfigPath .\infrastructure.yml -DryRun +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory = $false)] + [ValidateScript({ Test-Path $_ })] + [string]$ConfigPath = (Join-Path $PSScriptRoot "../../configs/infrastructure.yml"), + + [switch]$DryRun +) + +# Load configuration +. "$PSScriptRoot/../utilities/helpers/config-loader.ps1" +$config = Get-InfrastructureConfig -ConfigPath $ConfigPath + +# Extract registry variables +$clusterName = $config.compute.azure_local.cluster_name +$nodeCount = $config.compute.azure_local.cluster_node_count +$nodes = $config.compute.cluster_nodes +$networkIntents = $config.networking.onprem.network_intents +$localAdminUsername = $config.identity.accounts.account_local_admin_username +$localAdminPassword = $config.identity.accounts.account_local_admin_password + +# Validate registry constraints +if ($clusterName.Length -gt 15) { + throw "Cluster name '$clusterName' exceeds registry maxLength of 15 characters" +} + +if ($nodeCount -lt 1 -or $nodeCount -gt 16) { + throw "Node count $nodeCount outside registry range 1-16" +} + +if ($nodes.Count -ne $nodeCount) { + throw "Node count mismatch: configured $nodeCount nodes but defined $($nodes.Count) nodes" +} + +# Validate network intent +$managementIntent = $networkIntents | Where-Object { $_.name -match 'Management' } | Select-Object -First 1 +if (-not $managementIntent.subnet) { + throw "Management network intent missing required subnet_cidr" +} + +Write-Host "[VALIDATE] Configuration Validation Complete" +Write-Host " Cluster: $clusterName" +Write-Host " Nodes: $nodeCount" +Write-Host " Management Subnet: $($managementIntent.subnet)" + +if ($DryRun) { + Write-Host "[DRY RUN] Dry run mode - validation only" + return +} + +# Deployment logic would go here +Write-Host "[DEPLOY] Starting deployment..." + +foreach ($nodeName in $nodes.Keys) { + $node = $nodes[$nodeName] + Write-Host " Processing node: $($node.hostname) at $($node.management_ip)" + # Example deployment steps + # - Connect to node + # - Configure network + # - Join domain + # - Install Azure Local +} + +Write-Host "[SUCCESS] Deployment completed successfully" +``` + +### Python Validation Script + +```python +#!/usr/bin/env python3 +""" +Validate infrastructure configuration against registry schema. + +This script demonstrates how to validate infrastructure.yml files +using the JSON schema generated from the master registry. + +Variables validated: +- All required fields present +- Data types match registry definitions +- Values conform to constraints (ranges, patterns, enums) +- Cross-field validation rules +""" + +import yaml +import json +import jsonschema +import sys +from pathlib import Path +from typing import Dict, Any, List + +def load_config(config_path: str) -> Dict[str, Any]: + """Load infrastructure.yml configuration.""" + with open(config_path) as f: + return yaml.safe_load(f) + +def load_schema(schema_path: str) -> Dict[str, Any]: + """Load JSON schema for validation.""" + with open(schema_path) as f: + return json.load(f) + +def validate_config(config: Dict[str, Any], schema: Dict[str, Any]) -> List[str]: + """Validate configuration against schema.""" + errors = [] + + try: + jsonschema.validate(config, schema) + except jsonschema.ValidationError as e: + errors.append(f"Schema validation failed: {e.message}") + errors.append(f"Path: {'/'.join(str(p) for p in e.absolute_path)}") + except jsonschema.SchemaError as e: + errors.append(f"Schema is invalid: {e.message}") + + # Custom validation rules + errors.extend(validate_custom_rules(config)) + + return errors + +def validate_custom_rules(config: Dict[str, Any]) -> List[str]: + """Apply custom validation rules beyond JSON schema.""" + errors = [] + + # Validate cluster-node count consistency + if 'compute' in config and 'azure_local' in config['compute']: + cluster = config['compute']['azure_local'] + if 'cluster_node_count' in cluster: + expected_count = cluster['cluster_node_count'] + actual_count = len(config.get('compute', {}).get('cluster_nodes', {})) + if actual_count != expected_count: + errors.append( + f"Node count mismatch: configured {expected_count} nodes " + f"but defined {actual_count} nodes" + ) + + # Validate network intent requirements + if 'compute' in config and 'azure_local' in config['compute']: + cluster = config['compute']['azure_local'] + node_count = cluster.get('cluster_node_count', 0) + if node_count > 1: + network_intents = config.get('networking', {}).get('onprem', {}).get('network_intents', []) + has_storage = any(i for i in network_intents if 'Storage' in i.get('traffic_types', [])) + if not has_storage: + errors.append( + "Multi-node clusters require storage network intent" + ) + + return errors + +def main(): + if len(sys.argv) != 2: + print("Usage: validate_config.py ") + sys.exit(1) + + config_file = sys.argv[1] + schema_file = "variables/infrastructure.schema.json" + + # Load files + try: + config = load_config(config_file) + schema = load_schema(schema_file) + except FileNotFoundError as e: + print(f"Error: {e}") + sys.exit(1) + except yaml.YAMLError as e: + print(f"Error parsing YAML: {e}") + sys.exit(1) + + # Validate + errors = validate_config(config, schema) + + if errors: + print("[ERROR] Validation failed:") + for error in errors: + print(f" {error}") + sys.exit(1) + else: + print("[SUCCESS] Validation successful") + print(f" Configuration: {config_file}") + print(f" Schema: {schema_file}") + +if __name__ == "__main__": + main() +``` + +### Bash Configuration Generator + +```bash +#!/bin/bash + +# Generate cluster configuration documentation from infrastructure.yml +# This script demonstrates how to extract registry variables for documentation + +set -e + +CONFIG_FILE="infrastructure.yml" +OUTPUT_FILE="cluster-configuration.md" + +if [ ! -f "$CONFIG_FILE" ]; then + echo "Error: $CONFIG_FILE not found" + exit 1 +fi + +# Extract variables using yq (YAML processor) +CLUSTER_NAME=$(yq '.compute.azure_local.cluster_name' "$CONFIG_FILE") +NODE_COUNT=$(yq '.compute.azure_local.cluster_node_count' "$CONFIG_FILE") +ENVIRONMENT=$(yq '.environment.name' "$CONFIG_FILE") +DOMAIN=$(yq '.identity.active_directory.ad_domain_fqdn // "N/A"' "$CONFIG_FILE") + +# Generate documentation +cat > "$OUTPUT_FILE" << EOF +# Cluster Configuration + +**Environment:** $ENVIRONMENT +**Generated:** $(date) +**Source:** $CONFIG_FILE + +## Cluster Information + +- **Cluster Name:** $CLUSTER_NAME +- **Node Count:** $NODE_COUNT +- **Domain:** $DOMAIN + +## Cluster Nodes + +EOF + +# Extract node information +yq -r '.compute.cluster_nodes | to_entries[] | "- \(.value.hostname): \(.value.management_ip)"' "$CONFIG_FILE" >> "$OUTPUT_FILE" + +cat >> "$OUTPUT_FILE" << EOF + +## Network Configuration + +### Management Network +EOF + +yq -r '.networking.onprem.network_intents[0] | "- Subnet: \(.subnet)\n- Gateway: \(.gateway)\n- VLAN: \(.vlan_id)"' "$CONFIG_FILE" >> "$OUTPUT_FILE" + +echo "[SUCCESS] Generated $OUTPUT_FILE" +``` + +--- + +## IaC Template Examples + +### Bicep Template + +```bicep +// cluster-deployment.bicep +// Demonstrates using registry variables in Bicep templates + +@description('Infrastructure configuration from registry') +param config object + +// Extract registry variables +var clusterName = config.compute.azure_local.cluster_name +var nodeCount = config.compute.azure_local.cluster_node_count +var resourceGroupName = config.azure_platform.resource_group_name +var environment = config.environment.name +var tenantId = config.azure_platform.tenant.id + +// Validate constraints (Bicep parameter validation) +@maxLength(15) +param clusterNameValidation string = clusterName + +@minValue(1) +@maxValue(16) +param nodeCountValidation int = nodeCount + +// Resource group +resource clusterRG 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: 'eastus' + tags: { + Environment: environment + Cluster: clusterName + ManagedBy: 'Infinite Improbability Corp' + } +} + +// Storage account for cloud witness +resource witnessStorage 'Microsoft.Storage/storageAccounts@2021-09-01' = { + name: '${replace(clusterName, '-', '')}witness' + location: clusterRG.location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: { + allowBlobPublicAccess: false + minimumTlsVersion: 'TLS1_2' + supportsHttpsTrafficOnly: true + } +} + +// Output registry-compliant information +output clusterName string = clusterName +output nodeCount int = nodeCount +output resourceGroupName string = clusterRG.name +output witnessStorageName string = witnessStorage.name +``` + +### Terraform Configuration + +```hcl +# main.tf - Azure Local cluster deployment using registry variables + +variable "config" { + description = "Infrastructure configuration from registry" + type = object({ + compute = object({ + azure_local = object({ + cluster_name = string + cluster_node_count = number + arc_resource_group = string + }) + cluster_nodes = map(object({ + hostname = string + management_ip = string + idrac_ip = string + })) + }) + azure_platform = object({ + resource_group_name = string + }) + environment = object({ + name = string + }) + }) + + validation { + condition = length(var.config.compute.azure_local.cluster_name) <= 15 + error_message = "Cluster name must be 15 characters or less (NetBIOS limit)" + } + + validation { + condition = var.config.compute.azure_local.cluster_node_count >= 1 && var.config.compute.azure_local.cluster_node_count <= 16 + error_message = "Node count must be between 1 and 16" + } +} + +# Resource group +resource "azurerm_resource_group" "cluster" { + name = var.config.azure_platform.resource_group_name + location = "East US" + + tags = { + Environment = var.config.environment.name + Cluster = var.config.compute.azure_local.cluster_name + ManagedBy = "Infinite Improbability Corp" + } +} + +# Storage account for cloud witness +resource "azurerm_storage_account" "witness" { + name = replace(var.config.compute.azure_local.cluster_name, "-", "") + "witness" + resource_group_name = azurerm_resource_group.cluster.name + location = azurerm_resource_group.cluster.location + account_tier = "Standard" + account_replication_type = "LRS" + + # Registry-compliant settings + min_tls_version = "TLS1_2" + enable_https_traffic_only = true + allow_blob_public_access = false +} + +# Outputs (registry variable references) +output "cluster_name" { + description = "Azure Local cluster name" + value = var.config.compute.azure_local.cluster_name +} + +output "node_count" { + description = "Number of cluster nodes" + value = var.config.compute.azure_local.cluster_node_count +} + +output "resource_group_name" { + description = "Cluster resource group" + value = azurerm_resource_group.cluster.name +} +``` + +--- + +## CI/CD Pipeline Examples + +### GitHub Actions Workflow + +```yaml +# .github/workflows/deploy-azure-local.yml +name: Deploy Azure Local Cluster + +on: + push: + branches: [ main ] + paths: + - 'infrastructure.yml' + workflow_dispatch: + +env: + CONFIG_FILE: infrastructure.yml + SCHEMA_FILE: variables/infrastructure.schema.json + +jobs: + validate: + name: Validate Configuration + runs-on: windows-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup PowerShell + uses: microsoft/setup-powershell@v1 + + - name: Download Schema + run: | + $schemaUrl = "https://raw.githubusercontent.com/tierpoint/tierpoint-prodtech-documentation/main/docs/standards/variable-management/assets/infrastructure.schema.json" + Invoke-WebRequest -Uri $schemaUrl -OutFile $env:SCHEMA_FILE + + - name: Validate Configuration + run: | + .\tools\Validate-Infrastructure.ps1 -ConfigPath $env:CONFIG_FILE + + deploy: + name: Deploy Cluster + needs: validate + runs-on: windows-latest + environment: production + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup PowerShell + uses: microsoft/setup-powershell@v1 + + - name: Load Configuration + run: | + $config = Get-Content $env:CONFIG_FILE -Raw | ConvertFrom-Yaml + $clusterName = $config.compute.azure_local.cluster_name + Write-Host "##vso[task.setvariable variable=clusterName]$clusterName" + + - name: Deploy Azure Local + run: | + .\tools\Deploy-AzureLocal.ps1 -ConfigPath $env:CONFIG_FILE + env: + AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} + LOCAL_ADMIN_PASSWORD: ${{ secrets.LOCAL_ADMIN_PASSWORD }} + DOMAIN_ADMIN_PASSWORD: ${{ secrets.DOMAIN_ADMIN_PASSWORD }} +``` + +### Azure DevOps Pipeline + +```yaml +# azure-pipelines.yml +trigger: + branches: + include: + - main + paths: + include: + - infrastructure.yml + +variables: + configFile: 'infrastructure.yml' + schemaFile: 'variables/infrastructure.schema.json' + +stages: +- stage: Validate + jobs: + - job: ValidateConfig + pool: + vmImage: 'windows-latest' + steps: + - checkout: self + + - task: PowerShell@2 + name: DownloadSchema + inputs: + targetType: 'inline' + script: | + $schemaUrl = "https://raw.githubusercontent.com/tierpoint/tierpoint-prodtech-documentation/main/docs/standards/variable-management/assets/infrastructure.schema.json" + Invoke-WebRequest -Uri $schemaUrl -OutFile "$(schemaFile)" + + - task: PowerShell@2 + name: ValidateConfig + inputs: + targetType: 'inline' + script: | + .\tools\Validate-Infrastructure.ps1 -ConfigPath "$(configFile)" + +- stage: Deploy + dependsOn: Validate + condition: succeeded() + jobs: + - deployment: DeployCluster + environment: 'production' + pool: + vmImage: 'windows-latest' + strategy: + runOnce: + deploy: + steps: + - checkout: self + + - task: PowerShell@2 + name: LoadConfig + inputs: + targetType: 'inline' + script: | + $config = Get-Content "$(configFile)" -Raw | ConvertFrom-Yaml + $clusterName = $config.compute.azure_local.cluster_name + Write-Host "##vso[task.setvariable variable=clusterName]$clusterName" + + - task: PowerShell@2 + name: DeployCluster + inputs: + targetType: 'inline' + script: | + .\tools\Deploy-AzureLocal.ps1 -ConfigPath "$(configFile)" + env: + AZURE_CREDENTIALS: $(AZURE_CREDENTIALS) +``` + +--- + +## Validation Error Examples + +### Type Mismatch Errors + +**Configuration:** +```yaml +compute: + azure_local: + cluster_node_count: "4" # String instead of integer +``` + +**Error:** +``` +[ERROR] Validation failed: 1 error found + +Error 1: /compute/azure_local/cluster_node_count - Expected integer, got string "4" + Location: infrastructure.yml:12 + Expected: integer + Actual: "4" +``` + +**Fix:** +```yaml +compute: + azure_local: + cluster_node_count: 4 # Correct: integer +``` + +### Pattern Violation Errors + +**Configuration:** +```yaml +compute: + azure_local: + cluster_name: "my-cluster-name" # Contains hyphens +``` + +**Error:** +``` +[ERROR] Validation failed: 1 error found + +Error 1: /compute/azure_local/cluster_name - Does not match pattern ^[a-z0-9-]*$ + Location: infrastructure.yml:10 + Expected: Pattern ^[a-z0-9-]*$ + Actual: "my-cluster-name" +``` + +**Fix:** +```yaml +compute: + azure_local: + cluster_name: "myclustername" # Correct: no hyphens +``` + +### Range Violation Errors + +**Configuration:** +```yaml +compute: + azure_local: + cluster_node_count: 20 # Exceeds maximum +``` + +**Error:** +``` +[ERROR] Validation failed: 1 error found + +Error 1: /compute/azure_local/cluster_node_count - Value 20 exceeds maximum of 16 + Location: infrastructure.yml:12 + Expected: integer ≤ 16 + Actual: 20 +``` + +**Fix:** +```yaml +compute: + azure_local: + cluster_node_count: 4 # Correct: within range +``` + +### Missing Required Field Errors + +**Configuration:** +```yaml +# Missing azure_platform section +compute: + azure_local: + cluster_name: "test-cluster" +``` + +**Error:** +``` +[ERROR] Validation failed: 1 error found + +Error 1: Missing required property: azure_platform + Location: infrastructure.yml (root) + Expected: azure_platform object + Actual: missing +``` + +**Fix:** +```yaml +azure_platform: + tenant: + id: "00000000-aaaa-bbbb-cccc-111111111111" + name: "My Tenant" + +compute: + azure_local: + cluster_name: "test-cluster" +``` + +--- + +## Migration Examples + +### Converting Legacy Scripts + +**Before (Hardcoded):** +```powershell +# Legacy script with hardcoded values +$clusterName = "my-cluster" +$nodeCount = 4 +$resourceGroup = "rg-my-cluster" +$location = "eastus" + +# Deployment logic... +``` + +**After (Registry-based):** +```powershell +# Registry-compliant script +$config = Get-Content .\infrastructure.yml -Raw | ConvertFrom-Yaml + +$clusterName = $config.compute.azure_local.cluster_name +$nodeCount = $config.compute.azure_local.cluster_node_count +$resourceGroup = $config.azure_platform.resource_group_name +$location = $config.azure_platform.region + +# Deployment logic... +``` + +### Updating Legacy Configurations + +**Legacy infrastructure.yml:** +```yaml +# Old format +cluster: + name: "my-cluster" + nodes: 4 + location: "eastus" + +network: + management_subnet: "192.168.1.0/24" + storage_subnet: "10.71.1.0/24" +``` + +**Migrated to Registry (v4.0.0 13-section hierarchy):** +```yaml +# Registry-compliant format +azure_platform: + tenant: + id: "00000000-aaaa-bbbb-cccc-111111111111" + name: "My Tenant" + region: "eastus" + +compute: + azure_local: + cluster_name: "my-cluster" + cluster_node_count: 4 + cluster_nodes: + node01: + hostname: "my-node01" + management_ip: "192.168.1.11" + node02: + hostname: "my-node02" + management_ip: "192.168.1.12" + node03: + hostname: "my-node03" + management_ip: "192.168.1.13" + node04: + hostname: "my-node04" + management_ip: "192.168.1.14" + +networking: + onprem: + network_intents: + - name: "ManagementCompute" + traffic_types: [Management, Compute] + subnet: "192.168.1.0/24" + - name: "Storage" + traffic_types: [Storage] + storage_networks: + - subnet: "10.71.1.0/24" + vlan_id: 711 +``` + +--- + +## Downloads + +- [**📂 Master Registry**](./assets/master-registry.yaml) +- [**📂 JSON Schema**](./assets/infrastructure.schema.json) + +--- + +## Related Documents + +- [Registry Reference](./registry-reference.mdx) - Variable definitions +- [Schema Validation](./schema-validation.mdx) - Validation guide +- [Usage Workflows](./usage-workflows.mdx) - Implementation guide +- [Governance](./governance.mdx) - Process management +- [Technical Standards](./technical-standards.mdx) - Naming and types + +--- + +*Examples maintained by Product Technology team. Last updated: 2026-02-08* \ No newline at end of file diff --git a/standards/variable-management/governance.mdx b/standards/variable-management/governance.mdx new file mode 100644 index 000000000..ac67e5dc3 --- /dev/null +++ b/standards/variable-management/governance.mdx @@ -0,0 +1,469 @@ +--- +id: governance +title: Variable Governance +sidebar_label: Governance +--- + +# Variable Governance + +[![Governance](https://img.shields.io/badge/Type-Governance-blue?logo=github)](https://github.com/features/issues) +[![Process](https://img.shields.io/badge/Process-Standardized-green)](https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/) +[![Lifecycle](https://img.shields.io/badge/Lifecycle-Management-purple)](https://semver.org/) + +> **Processes for managing the variable registry lifecycle - from proposal to deployment** + +**Process Guide** | **Registry Maintenance** | **Governance Workflows** + +--- + +## Overview + +Variable governance ensures the master registry remains accurate, consistent, and useful across all TierPoint Product Technology repositories. This document defines the processes for proposing, reviewing, approving, and deploying changes to the variable system. + +### Governance Principles + +1. **Single Source of Truth** - Registry is the authoritative reference +2. **Collaborative Review** - Changes require team approval +3. **Version Control** - Semantic versioning for changes +4. **Documentation** - All changes documented and communicated +5. **Validation** - Automated testing prevents breaking changes + +--- + +## Registry Change Process + +### Phase 1: Proposal + +**Who:** Any team member +**Trigger:** Need for new variable or change to existing variable + +**Steps:** +1. **Check for Duplicates** + ```bash + # Search registry for similar variables + grep -i "cluster.*name" master-registry.yaml + ``` + +2. **Create Issue** + - Repository: `tierpoint-prodtech-documentation` + - Template: `Variable Registry Change` + - Fields: + - Variable name + - Category + - Type and constraints + - Use case justification + - Impact assessment + +3. **Draft Variable Definition** + ```yaml + # Proposed addition to master-registry.yaml + clusters: + azure_local: + azl_custom_setting: + type: string + description: "Custom setting for specific use case" + required: false + example: "default-value" + ``` + +### Phase 2: Review + +**Who:** Product Technology Architecture Team +**Duration:** 2-5 business days + +**Review Criteria:** +- **Necessity** - Is this variable actually needed? +- **Naming** - Follows registry naming conventions? +- **Scope** - Universal or environment-specific? +- **Constraints** - Appropriate validation rules? +- **Documentation** - Clear description and examples? +- **Impact** - Breaking changes to existing repos? + +**Review Checklist:** +```markdown +- [ ] Variable name follows snake_case convention +- [ ] Description is clear and actionable +- [ ] Type constraints are appropriate +- [ ] Examples provided for complex variables +- [ ] No duplicates in registry +- [ ] Impact on existing repositories assessed +- [ ] Schema generation tested +``` + +### Phase 3: Approval + +**Who:** Product Technology Lead or Architecture Team +**Trigger:** Review completed with no blocking issues + +**Approval Process:** +1. **Technical Review** - Code review of registry changes +2. **Impact Review** - Assessment of downstream effects +3. **Documentation Review** - Updates to this governance document +4. **Final Approval** - Merge authorization + +### Phase 4: Implementation + +**Who:** Registry maintainer +**Trigger:** Change approved + +**Steps:** +1. **Update Registry** + ```bash + # Edit master-registry.yaml + vim docs/standards/variable-management/assets/master-registry.yaml + ``` + +2. **Regenerate Schema** + ```bash + # Generate updated JSON schema + generate-schema --input master-registry.yaml --output infrastructure.schema.json + ``` + +3. **Update Documentation** + - Update registry reference + - Update examples if needed + - Update version numbers + +4. **Create Pull Request** + - Branch: `feature/variable-{name}` + - Description: Links to original issue + - Reviewers: Architecture team + +### Phase 5: Deployment + +**Who:** CI/CD Pipeline +**Trigger:** PR merged + +**Automated Steps:** +1. **Version Bump** + ```bash + # Update version based on change type + # Major: Breaking changes + # Minor: New variables + # Patch: Documentation updates + ``` + +2. **Validation Tests** + ```bash + # Test schema generation + validate-schema infrastructure.schema.json + + # Test against sample configurations + validate-config --schema infrastructure.schema.json --config sample-infrastructure.yml + ``` + +3. **Documentation Build** + ```bash + # Build Docusaurus site + npm run build + ``` + +4. **Notification** + - Slack notification to ProdTech team + - Email to repository maintainers + - Update to change log + +--- + +## Change Types and Versioning + +### Semantic Versioning + +**Major Version (2.x.x → 3.x.x)** +- Breaking changes (removed variables, changed types) +- Requires updates to all environment repositories +- Communication: Email to all teams, migration guide + +**Minor Version (x.2.x → x.3.x)** +- New variables or categories added +- Backward compatible +- Communication: Slack notification, documentation update + +**Patch Version (x.x.1 → x.x.2)** +- Documentation updates, examples, metadata fixes +- No functional changes +- Communication: Internal documentation update + +### Change Classification + +| Change Type | Version Impact | Review Required | Communication | +|-------------|----------------|-----------------|---------------| +| **New Variable** | Minor | Architecture Team | Team notification | +| **Type Change** | Major | All stakeholders | Organization-wide | +| **Constraint Change** | Major | Architecture Team | Team notification | +| **Documentation** | Patch | Self-review | None | +| **Example Update** | Patch | Self-review | None | + +--- + +## Repository Synchronization + +### Distribution Model + +**Centralized Registry → Distributed Copies** + +``` +Master Registry (Documentation Repo) + ↓ Sync +Environment Repositories + ↓ Validation +Infrastructure Deployment +``` + +### Sync Process + +**Automated Sync (Recommended):** +```yaml +# .github/workflows/sync-registry.yml +name: Sync Variable Registry + +on: + schedule: + - cron: '0 2 * * 1' # Weekly Monday 2 AM + workflow_dispatch: + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Download Latest Registry + run: | + curl -o variables/master-registry.yaml \ + https://raw.githubusercontent.com/tierpoint/tierpoint-prodtech-documentation/main/docs/standards/variable-management/assets/master-registry.yaml + + curl -o variables/infrastructure.schema.json \ + https://raw.githubusercontent.com/tierpoint/tierpoint-prodtech-documentation/main/docs/standards/variable-management/assets/infrastructure.schema.json + + - name: Validate Sync + run: | + # Test schema validity + ajv validate -s variables/infrastructure.schema.json -d infrastructure.yml + + - name: Create PR + uses: peter-evans/create-pull-request@v4 + with: + title: 'chore: sync variable registry ${{ github.event.repository.updated_at }}' + body: 'Automated sync of master variable registry' +``` + +**Manual Sync:** +```powershell +# Download latest registry +Invoke-WebRequest -Uri "https://tierpoint-prodtech-documentation/docs/standards/variable-management/assets/master-registry.yaml" ` + -OutFile "variables/master-registry.yaml" + +Invoke-WebRequest -Uri "https://tierpoint-prodtech-documentation/docs/standards/variable-management/assets/infrastructure.schema.json" ` + -OutFile "variables/infrastructure.schema.json" + +# Validate +.\tools\Validate-Infrastructure.ps1 -ConfigPath .\infrastructure.yml +``` + +### Sync Frequency + +| Repository Type | Sync Frequency | Trigger | +|----------------|----------------|---------| +| **Active Development** | Daily | CI/CD Pipeline | +| **Stable Production** | Weekly | Scheduled Job | +| **Archived** | Manual | As needed | + +--- + +## Quality Assurance + +### Pre-Merge Checks + +**Automated:** +```yaml +# CI/CD Pipeline +- name: Validate Registry Changes + run: | + # Check YAML syntax + yamllint master-registry.yaml + + # Validate schema generation + generate-schema --input master-registry.yaml --output temp-schema.json + diff infrastructure.schema.json temp-schema.json + + # Test against sample configs + ajv validate -s infrastructure.schema.json -d sample-infrastructure.yml +``` + +**Manual:** +- [ ] Registry YAML is valid +- [ ] Schema generates without errors +- [ ] Sample configurations validate +- [ ] Documentation builds successfully +- [ ] No breaking changes without major version bump + +### Post-Merge Validation + +**Environment Testing:** +```powershell +# Validate the deployment configuration +$configFile = "configs/infrastructure.yml" +Write-Host "Validating $configFile..." + +# Validate against new schema +.\tools\Validate-Infrastructure.ps1 -ConfigPath $configFile + +# Test deployment scripts (dry run) +.\tools\Test-Deployment.ps1 -ConfigPath $configFile -DryRun +``` + +--- + +## Emergency Changes + +### When to Use Emergency Process + +- **Security Issues** - Immediate security fixes required +- **Critical Bugs** - Blocking production deployments +- **Compliance Requirements** - Regulatory or audit requirements + +### Emergency Process + +1. **Immediate Fix** - Apply critical change directly +2. **Documentation** - Record emergency change with justification +3. **Review** - Post-change review within 24 hours +4. **Communication** - Notify all affected teams +5. **Version Update** - Apply appropriate version bump + +### Emergency Change Log + +```yaml +# emergency-changes.yaml +- date: "2026-01-29" + change: "Fixed UUID validation pattern" + justification: "Blocking production deployments" + reviewer: "Security Team" + version_impact: "patch" + communication: "Email sent to all repository maintainers" +``` + +--- + +## Change Log Management + +### Change Log Format + +```yaml +# CHANGELOG.md +## [2.1.0] - 2025-12-13 + +### Added +- Added Hyper-V Host configuration variables (7 variables) +- Added Arc Resource Bridge for SCVMM integration (5 variables) + +### Changed +- Updated cluster node validation constraints + +### Fixed +- Corrected VLAN range validation (0-4094) +``` + +### Change Log Automation + +```bash +# Generate change log from git history +git log --oneline --grep="registry" --since="2025-12-01" | \ + grep -E "(Added|Changed|Fixed|Removed)" | \ + sort -r > changelog-registry.md +``` + +--- + +## Training and Documentation + +### New Team Member Onboarding + +**Required Reading:** +1. [Variable Management Overview](../index.mdx) +2. [Registry Reference](./registry-reference.mdx) +3. [Usage Workflows](./usage-workflows.mdx) +4. This governance document + +**Hands-on Training:** +- Create sample infrastructure.yml +- Validate against schema +- Propose new variable (simulated) + +### Documentation Updates + +**When to Update:** +- New registry features +- Process changes +- Common issues discovered +- Best practices identified + +**Update Process:** +1. Create documentation PR +2. Review by technical writers +3. Merge and deploy + +--- + +## Metrics and Monitoring + +### Registry Health Metrics + +```powershell +# Registry metrics script +$registry = Get-Content master-registry.yaml -Raw | ConvertFrom-Yaml + +$metrics = @{ + TotalVariables = ($registry | ConvertTo-Json -Depth 10).Split('"').Count / 2 + Categories = $registry.PSObject.Properties.Name.Count + RequiredVariables = ($registry | ConvertTo-Json -Depth 10 | Select-String '"required": true').Count + Version = $registry.version +} + +$metrics | ConvertTo-Json +``` + +### Adoption Metrics + +- **Repository Coverage** - % of repos using registry +- **Validation Success Rate** - % of validations passing +- **Change Frequency** - Variables added per month +- **Error Reduction** - Deployment failures prevented + +### Monitoring Dashboard + +```yaml +# Registry monitoring (proposed) +metrics: + - name: registry_sync_status + type: gauge + description: "Registry synchronization status across repositories" + + - name: validation_errors + type: counter + description: "Schema validation errors detected" + + - name: change_proposals + type: counter + description: "Variable change proposals submitted" +``` + +--- + +## Downloads + +- [**📂 Master Registry**](./assets/master-registry.yaml) +- [**📂 JSON Schema**](./assets/infrastructure.schema.json) + +--- + +## Related Documents + +- [Registry Reference](./registry-reference.mdx) - Variable definitions +- [Schema Validation](./schema-validation.mdx) - Validation guide +- [Usage Workflows](./usage-workflows.mdx) - Implementation guide +- [Examples](./examples.mdx) - Usage examples + +--- + +*Governance maintained by Product Technology team. Last updated: 2026-01-29* \ No newline at end of file diff --git a/standards/variable-management/index.mdx b/standards/variable-management/index.mdx new file mode 100644 index 000000000..a7b31aea8 --- /dev/null +++ b/standards/variable-management/index.mdx @@ -0,0 +1,103 @@ +--- +id: variable-management +title: Variable Management Standard +sidebar_label: Variable Management +--- + +# Variable Management Standard + +[![Standards-Variables](https://img.shields.io/badge/Standards-Variables-blue?logo=yaml)](https://yaml.org/) +[![Version-2.1.0](https://img.shields.io/badge/Version-2.1.0-green)](https://semver.org/) +[![Status-Active](https://img.shields.io/badge/Status-Active-success)](https://github.com/kristopherjturner/master-workplace-project) + +> **Infrastructure configuration management for Azure Local deployment engineers** + +**Purpose**: Standardize how customer deployment configurations are created, validated, and maintained. + +--- + +## What Is This? + +When deploying Azure Local for a customer, you need to manage hundreds of configuration values: tenant IDs, subscription IDs, resource names, network settings, node configurations, etc. This standard defines how to organize, validate, and use these values consistently. + +## Why Does This Matter? + +**Without standards**, every deployment uses different variable names, structures, and validation methods. This causes: +- Configuration errors during deployment +- Difficulty troubleshooting issues across sites +- Time wasted finding values in different formats +- Risk of incorrect deployments + +**With this standard**, you get: +- **One configuration file** (`infrastructure.yml`) per customer deployment +- **Validated structure** - catch errors before deployment +- **Self-documenting** - variable names are clear and consistent +- **Reusable scripts** - same automation works for all customers + +## How Does This Help Deployment Engineers? + +1. **Faster Deployments**: Fill out one YAML file, run validation, deploy. No hunting for values across multiple sources. +2. **Fewer Errors**: Schema validation catches typos, missing values, and incorrect formats before you deploy. +3. **Easier Troubleshooting**: Consistent variable names mean logs and errors are clear across all deployments. +4. **Knowledge Transfer**: New engineers can read `infrastructure.yml` and understand the deployment immediately. + +## Quick Start for Deployment + +1. Copy `infrastructure.yml` template for your customer +2. Fill in customer-specific values (tenant ID, subscription, cluster name, etc.) +3. Run validation: `./Validate-Infrastructure.ps1` +4. Fix any errors reported +5. Run deployment scripts (they all read from `infrastructure.yml`) + +--- + +## Documentation in This Section + +| Document | Purpose | When To Use | +|----------|---------|-------------| +| [Registry Reference](./registry-reference.mdx) | Complete list of all available variables | Setting up new deployment | +| [Schema Validation](./schema-validation.mdx) | How to validate your config file | Before every deployment | +| [Usage Workflows](./usage-workflows.mdx) | How scripts read configuration | Understanding automation | +| [Technical Standards](./technical-standards.mdx) | Naming rules and patterns | Creating custom variables | +| [Examples](./examples.mdx) | Real customer deployment examples | Learning by example | +| [Troubleshooting](./troubleshooting.mdx) | Fix common configuration errors | When validation fails | + +--- + +## Relationship to Scripting Standards + +:::info +The master-registry and infrastructure schema are **not just for documentation** — they are the **mandatory source of truth for all scripts**. Every YAML path used in any PowerShell or Bash script must exist in the schema. No exceptions. +::: + +| Standard | Document | What It Covers | +|----------|----------|---------------| +| **Variable paths in scripts** | [Scripting Standards — Variable Path Compliance](../scripting/scripting-standards.mdx#variable-path-compliance--master-registry--schema) | Rules for using correct YAML paths in scripts | +| **Variable path contract** | [Scripting Framework — Variable Path Contract](../scripting/scripting-framework.mdx#the-variable-path-contract) | Schema-to-script path mapping and quick reference | +| **Schema validation** | [Schema Validation](./schema-validation.mdx) | How to validate `infrastructure.yml` before deployment | + +If a script uses a YAML path that doesn't exist in `infrastructure.schema.json`, the script is wrong — not the schema. Fix the script. + +--- + +## How To Maintain Standards + +**For Deployment Engineers:** +- Always use the variable names defined in the registry +- **Verify every YAML path against `infrastructure.schema.json` before using it in a script** +- Run validation before every deployment +- Report missing or unclear variables to documentation team + +**For Script Authors (including AI):** +- **Never invent YAML paths** — look them up in the schema first +- Comment every `$cfg.*` access with the full dotted path +- See [Scripting Standards](../scripting/scripting-standards.mdx#variable-path-compliance--master-registry--schema) for the complete rules + +**For Documentation Team:** +- Update registry when new Azure Local features require new variables +- Test schema validation with real customer deployments +- Keep examples current with actual customer configurations + +--- + +*This toolkit is for Azure Local deployment engineers. For questions, contact your team lead.* \ No newline at end of file diff --git a/standards/variable-management/registry-reference.mdx b/standards/variable-management/registry-reference.mdx new file mode 100644 index 000000000..399728d6c --- /dev/null +++ b/standards/variable-management/registry-reference.mdx @@ -0,0 +1,366 @@ +--- +id: registry-reference +title: Master Variable Registry Reference +sidebar_label: Registry Reference +--- + +# Master Variable Registry Reference + +[![Registry](https://img.shields.io/badge/Type-Registry-blue?logo=yaml)](https://yaml.org/) +[![Version-4.0.0](https://img.shields.io/badge/Version-4.0.0-green)](https://semver.org/) +[![Variables-970+](https://img.shields.io/badge/Variables-970+-purple)](https://github.com/kristopherjturner/master-workplace-project) + +> **Complete reference for the master variable registry - the single source of truth for all variable definitions** + +**Complete Variable Reference** | **Authoritative Definitions** | **Version 4.0.0** + +--- + +## Overview + +The Master Variable Registry is the single source of truth for all variable definitions used across TierPoint Product Technology repositories. It contains 970+ variables organized into **13 hierarchical sections**, each with detailed metadata including types, constraints, descriptions, and examples. + +### Registry Location + +**File**: [`./assets/master-registry.yaml`](./assets/master-registry.yaml) + +**Version**: 4.0.0 +**Last Updated**: 2026-03-05 +**Variables**: 970+ +**Sections**: 13 + +### Registry Structure + +The registry uses a 13-section hierarchy: + +```yaml +# 1. Metadata +_metadata: # File versioning and change tracking + +# 2. Infrastructure Scenarios +infrastructure_scenarios: # Deployment type taxonomy + +# 3-5. Core Identity +site: # Physical location + hardware +environment: # Environment classification +tags: # Azure resource tagging standards + +# 6. Azure Platform +azure_platform: + azure_tenants: # Azure AD/Entra ID tenants + management_groups: # Management group hierarchy + subscriptions: # Azure subscriptions + resource_groups: # Resource group definitions + +# 7. Identity +identity: + accounts: # Service accounts, credentials, SPs + active_directory: # AD domain settings + managed_identities: # User/system-assigned MIs + b2b_configuration: # Cross-tenant B2B access + +# 8. Networking +networking: + azure: # Azure networking + hub_spoke: # Hub-spoke architecture + vnets: # Virtual networks + bastion: # Bastion hosts + firewall: # Azure Firewall rules + sdn: # Software Defined Networking + onprem: # On-premises networking + vlans: # VLAN configurations + dhcp: # DHCP settings + network_intents: # Network intents + logical_networks: # Logical networks + network_devices: # Switches, ToR devices + hybrid: # Hybrid connectivity + vpn: # VPN configurations + +# 9. Compute +compute: + vms: # Virtual machines + azure: # Azure VMs (DCs, utility, etc.) + arc_hybrid: # Arc-enabled hybrid VMs + clusters: # Azure Local / WSFC clusters + cluster_nodes: # Individual node definitions + cluster_arm_deployment: # ARM deployment parameters + avd: # Azure Virtual Desktop + +# 10. Storage +storage_accounts: # Azure Storage Accounts + +# 11. Security +security: + keyvault: # Key Vault configuration + defender: # Defender for Cloud + sentinel: # Azure Sentinel SIEM/SOAR + +# 12. Operations +operations: + monitoring: # Monitoring + Log Analytics + policy: # Azure Policy + azure_update_manager: # OS patching + bcdr: # Backup/disaster recovery + scvmm: # SCVMM integration + +# 13. DevOps +devops: + gitlab: # GitLab CI/CD + terraform: # Terraform state/providers +``` + +--- + +## Variable Categories + +### 1. Infrastructure Scenarios + +Defines deployment scenarios and infrastructure types. + +**Key Variables:** +- `azure_local`: Azure Local (Azure Stack HCI) clusters +- `wsfc_s2d`: Windows Server Failover Clustering with Storage Spaces Direct +- `wsfc_san`: Windows Server Failover Clustering with SAN storage +- `avd_azure_local`: Azure Virtual Desktop on Azure Local +- `scvmm`: System Center Virtual Machine Manager integration + +### 2. Site + +Physical datacenter location, site metadata, and hardware details. + +| Variable | Type | Required | Description | +|----------|------|----------|-------------| +| `site_code` | String | Yes | Short site identifier | +| `site_name` | String | Yes | Full site name | +| `site_location` | String | Yes | Geographic location | +| `hardware.hw_vendor` | String | No | Hardware vendor (Dell, etc.) | +| `hardware.hw_model` | String | No | Server model | + +### 3. Environment + +Environment identification and metadata. + +### 4. Tags + +Azure resource tagging standards. + +### 5. Azure Platform (`azure_platform`) + +Azure organizational hierarchy — tenants, management groups, subscriptions, resource groups. + +| Sub-section | Key Variables | +|-------------|---------------| +| `azure_tenants` | `aztenant_azurelocal_id`, `aztenant_azurelocal_name` | +| `management_groups` | Management group hierarchy definitions | +| `subscriptions` | `sub_bootstrap_id`, subscription IDs | +| `resource_groups` | `rg_connectivity_hub`, resource group definitions | + +### 6. Identity (`identity`) + +Accounts, Active Directory, managed identities, and B2B configuration. + +| Sub-section | Key Variables | +|-------------|---------------| +| `accounts` | `account_local_admin_*`, `spn_*`, service principals | +| `active_directory` | Domain settings, OU structure | +| `managed_identities` | User-assigned and system-assigned MIs | +| `b2b_configuration` | Cross-tenant synchronization settings | + +### 7. Networking (`networking`) + +All networking organized by scope: Azure, on-premises, and hybrid. + +| Sub-section | Key Variables | +|-------------|---------------| +| `azure.hub_spoke` | `hub_vnet_name`, `hub_vnet_address_space` | +| `azure.vnets` | `vnet_name`, virtual network definitions | +| `azure.bastion` | Bastion host configuration | +| `azure.firewall` | Azure Firewall rules and policies | +| `azure.sdn` | Software Defined Networking config | +| `onprem.vlans` | VLAN definitions | +| `onprem.dhcp` | DHCP service settings | +| `onprem.network_intents` | Management, storage, compute intents | +| `onprem.logical_networks` | Logical network definitions | +| `onprem.network_devices` | Switches, ToR devices | +| `hybrid.vpn` | VPN gateway configuration | + +### 8. Compute (`compute`) + +Virtual machines, clusters, nodes, ARM deployment, and AVD. + +| Sub-section | Key Variables | +|-------------|---------------| +| `vms.azure` | `azure_vm_dc1_*`, `azure_vm_utility_*` | +| `vms.arc_hybrid` | Arc-enabled hybrid VM definitions | +| `clusters` | `cluster_name`, `cluster_node_count`, `arc_resource_group` | +| `cluster_nodes` | `node_hostname`, `node_management_ip`, `node_idrac_ip` | +| `cluster_arm_deployment` | ARM deployment parameters | +| `avd` | Azure Virtual Desktop configuration | + +### 9. Storage Accounts (`storage_accounts`) + +Azure Storage Account definitions for monitoring, diagnostics, witnesses, and flow logs. + +### 10. Security (`security`) + +Key Vault, Defender for Cloud, and Sentinel. + +| Sub-section | Key Variables | +|-------------|---------------| +| `keyvault` | Key Vault names, URIs, secret references | +| `defender` | Defender for Cloud plans and settings | +| `sentinel` | SIEM/SOAR workspace and connector config | + +### 11. Operations (`operations`) + +Monitoring, policy, patching, backup/recovery, and SCVMM. + +| Sub-section | Key Variables | +|-------------|---------------| +| `monitoring` | Log Analytics workspace, DCR rules | +| `policy` | Azure Policy assignments and definitions | +| `azure_update_manager` | OS patching schedules | +| `bcdr` | Backup and disaster recovery settings | +| `scvmm` | SCVMM server and Arc Resource Bridge config | + +### 12. DevOps (`devops`) + +GitLab CI/CD and Terraform configuration. + +| Sub-section | Key Variables | +|-------------|---------------| +| `gitlab` | `gitlab_project_id`, `gitlab_runner_token` | +| `terraform` | `tf_backend_storage_account`, `tf_provider_azurerm_version` | + +--- + +## Variable Metadata + +Each variable in the registry includes comprehensive metadata: + +### Required Metadata + +| Field | Description | Example | +|-------|-------------|---------| +| `type` | Data type | `string`, `integer`, `boolean`, `array`, `object` | +| `description` | Clear description | `"Azure Local cluster name"` | +| `required` | Whether mandatory | `true` or `false` | + +### Optional Metadata + +| Field | Description | Example | +|-------|-------------|---------| +| `format` | Specialized format | `uuid`, `ipv4`, `cidr`, `email`, `mac` | +| `pattern` | Regex pattern | `"^[a-z0-9-]+ $"` | +| `minLength` | Minimum string length | `1` | +| `maxLength` | Maximum string length | `15` | +| `minimum` | Minimum numeric value | `1` | +| `maximum` | Maximum numeric value | `16` | +| `allowedValues` | Enum values | `["eastus", "westus"]` | +| `sensitive` | Sensitive data flag | `true` | +| `example` | Usage example | `"iic-clus01"` | +| `note` | Additional context | `"NetBIOS compatible"` | + +--- + +## Usage Examples + +### Reading Cluster Configuration + +```powershell +# Load infrastructure.yml +$config = Get-Content .\infrastructure.yml -Raw | ConvertFrom-Yaml + +# Access registry-defined variables (v4.0.0 hierarchy paths) +$clusterName = $config.compute.azure_local.cluster_name +$nodeCount = $config.compute.azure_local.cluster_node_count +$nodes = $config.compute.cluster_nodes +``` + +### Validating Against Registry + +```powershell +# Validate cluster name length (registry constraint: maxLength: 15) +if ($clusterName.Length -gt 15) { + throw "Cluster name exceeds NetBIOS limit of 15 characters" +} +``` + +### Environment-Specific Values + +```yaml +# infrastructure.yml — environment-specific values (v4.0.0 structure) +compute: + azure_local: + cluster_name: "iic-clus01" # Environment-specific value + cluster_node_count: 2 # Registry: minimum: 1, maximum: 16 + cluster_nodes: + node01: + hostname: "iic-01-n01" + management_ip: "10.0.0.11" + node02: + hostname: "iic-01-n02" + management_ip: "10.0.0.12" +``` + +--- + +## Registry Maintenance + +### Version Control + +- **Major Version** (2.x.x): Breaking changes, removed variables +- **Minor Version** (x.2.x): New variables or categories +- **Patch Version** (x.x.2): Documentation updates, examples + +### Adding Variables + +1. **Check for Duplicates**: Search registry for similar variables +2. **Define Metadata**: Include all required and relevant optional fields +3. **Test Validation**: Ensure JSON schema generates correctly +4. **Update Documentation**: Add to this reference +5. **Submit PR**: Follow governance process + +### Change Log + +**Version 4.0.0 (2026-03-05):** +- **BREAKING**: Restructured from 40 flat sections → 13 hierarchical sections +- Dissolved 8 fragment sections (hardware_support, network_config, cluster_networking, cluster_config, cross_reference, monitoring_extended, snmp, arc_resource_bridge) +- Renamed: `bcdr_backup_recovery` → `operations.bcdr`, `storage` → `storage_accounts`, `azure_firewall` → `networking.azure.firewall`, `hub_spoke_networking` → `networking.azure.hub_spoke` +- Added: `security.sentinel`, `identity.managed_identities`, `identity.b2b_configuration`, `networking.azure.sdn`, `devops.terraform` +- All variables gap-analyzed against 3 live infrastructure files + 156 scripts + +**Version 2.2.0 (2026-02-01):** +- Added B2B cross-tenant synchronization (45+ variables) +- Added GitLab CI/CD configuration variables (4 variables) +- Added Arc Resource Bridge variables (3 variables) + +**Version 2.1.0 (2025-12-13):** +- Added Hyper-V Host configuration (7 variables) +- Added Arc Resource Bridge for SCVMM (5 variables) +- Added Arc Hybrid VM management (8 variables) +- Added Windows Failover Cluster enhancements (6 variables) +- Added SCVMM connection enhancements (3 variables) +- Added Azure Virtual Desktop in Azure cloud (15 variables) +- Added Azure Virtual Desktop on Azure Local (8 variables) +- Total: 52 new variables + +--- + +## Downloads + +- [**📂 Master Variable Registry YAML**](./assets/master-registry.yaml) +- [**📂 JSON Schema**](./schema-validation.mdx) + +--- + +## Related Documents + +- [Schema Validation](./schema-validation.mdx) - JSON Schema validation +- [Usage Workflows](./usage-workflows.mdx) - Implementation guide +- [Governance](./governance.mdx) - Variable lifecycle +- [Examples](./examples.mdx) - Real-world usage + +--- + +*Registry maintained by Product Technology team. Last updated: 2026-03-05* \ No newline at end of file diff --git a/standards/variable-management/schema-validation.mdx b/standards/variable-management/schema-validation.mdx new file mode 100644 index 000000000..043835798 --- /dev/null +++ b/standards/variable-management/schema-validation.mdx @@ -0,0 +1,372 @@ +--- +id: schema-validation +title: Schema Validation Guide +sidebar_label: Schema Validation +--- + +# Schema Validation Guide + +[![Schema](https://img.shields.io/badge/Type-Schema-blue?logo=json)](https://json-schema.org/) +[![Validation](https://img.shields.io/badge/Validation-JSON_Schema-green)](https://json-schema.org/) +[![Time-Saving](https://img.shields.io/badge/Time_Saved-28+min-orange)](https://github.com/kristopherjturner/master-workplace-project) + +> **JSON Schema validation for infrastructure.yml files - catch errors in seconds, not during deployment** + +**Validation Reference** | **Error Prevention** | **JSON Schema v7** + +--- + +## Overview + +JSON Schema validation provides automated validation of `infrastructure.yml` files against the master variable registry. This catches configuration errors immediately rather than during deployment, saving significant time and preventing failures. + +### Validation Benefits + +| Without Validation | With Validation | +|-------------------|------------------| +| Errors found after 30+ minutes deployment | Errors caught in 2 seconds | +| Manual debugging of deployment failures | Clear error messages with line numbers | +| Inconsistent variable usage across repos | Enforced consistency via schema | +| Type mismatches cause runtime errors | Type validation prevents issues | + +### Schema Location + +**File**: [`./assets/infrastructure.schema.json`](./assets/infrastructure.schema.json) + +**Generated From**: [`./assets/master-registry.yaml`](./assets/master-registry.yaml) +**Version**: Matches registry version 2.1.0 +**Format**: JSON Schema v7 + +--- + +## Validation Process + +### 1. Schema Generation + +The JSON Schema is automatically generated from the master registry: + +```bash +# Generate schema from registry +generate-schema --input master-registry.yaml --output infrastructure.schema.json +``` + +### 2. Validation Execution + +```powershell +# Validate infrastructure.yml against schema +.\tools\Validate-Infrastructure.ps1 -ConfigPath .\infrastructure.yml +``` + +### 3. Validation Checks + +The schema validates: + +- **Required Fields**: All mandatory variables present +- **Data Types**: Variables match defined types (string/integer/boolean/array/object) +- **Format Validation**: UUID, IPv4, CIDR, email, MAC address formats +- **Pattern Matching**: Regex validation for naming conventions +- **Range Constraints**: Min/max values for numbers and strings +- **Enum Values**: Variables restricted to allowed values +- **Array/Object Structure**: Nested object validation + +--- + +## Validation Examples + +### Successful Validation + +```powershell +# Output: Validation successful +[SUCCESS] infrastructure.yml conforms to schema +``` + +### Validation Errors + +```powershell +# Output: Validation failed with detailed errors +[ERROR] Validation failed: 3 errors found + +Error 1: /compute/azure_local/cluster_node_count - Value 20 exceeds maximum of 16 + Location: infrastructure.yml:15 + Expected: integer ≤ 16 + Actual: 20 + +Error 2: /compute/azure_local/cluster_name - String length 24 exceeds maxLength of 15 + Location: infrastructure.yml:12 + Expected: string ≤ 15 characters + Actual: "this-is-a-very-long-name" (24 characters) + +Error 3: /azure_platform/tenant/id - Value does not match UUID pattern + Location: infrastructure.yml:5 + Expected: UUID format (e.g., 00000000-aaaa-bbbb-cccc-111111111111) + Actual: "not-a-valid-uuid" +``` + +--- + +## Schema Structure + +### Top-Level Schema + +```json +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "infrastructure_scenarios": { "type": "object" }, + "azure_platform": { "type": "object" }, + "compute": { "type": "object" }, + // ... all 13 sections + }, + "required": ["azure_platform", "compute"] // Minimum required +} +``` + +### Variable Constraints + +**String with Pattern:** +```json +"cluster_name": { + "type": "string", + "description": "Azure Local cluster name", + "maxLength": 15, + "pattern": "^[a-zA-Z][a-zA-Z0-9-]*$" +} +``` + +**Integer with Range:** +```json +"cluster_node_count": { + "type": "integer", + "description": "Number of cluster nodes", + "minimum": 1, + "maximum": 16 +} +``` + +**UUID Format:** +```json +"id": { + "type": "string", + "description": "Azure tenant ID", + "format": "uuid" +} +``` + +**Enum Values:** +```json +"environment_type": { + "type": "string", + "enum": ["production", "lab", "demo", "poc", "management"] +} +``` + +--- + +## Common Validation Errors + +### Type Mismatches + +**Error:** `/compute/azure_local/cluster_node_count - Expected integer, got string "4"` + +**Fix:** +```yaml +# Wrong +cluster_node_count: "4" + +# Correct +cluster_node_count: 4 +``` + +### Pattern Violations + +**Error:** `/compute/azure_local/cluster_name - Does not match pattern ^[a-zA-Z][a-zA-Z0-9-]*$` + +**Fix:** +```yaml +# Wrong (starts with number) +cluster_name: "01-cluster" + +# Correct (follows -clus pattern) +cluster_name: "ral-clus01" +``` + +### Range Violations + +**Error:** `/compute/azure_local/cluster_node_count - Value 20 exceeds maximum of 16` + +**Fix:** +```yaml +# Wrong +cluster_node_count: 20 + +# Correct +cluster_node_count: 4 +``` + +### Missing Required Fields + +**Error:** `Missing required property: azure_platform` + +**Fix:** +```yaml +# Add missing section +azure_platform: + tenant: + id: "00000000-aaaa-bbbb-cccc-111111111111" + name: "TierPoint Labs" +``` + +### Format Violations + +**Error:** `/azure_platform/tenant/id - Value does not match UUID format` + +**Fix:** +```yaml +# Wrong +aztenant_azurelocal_id: "12345" + +# Correct +aztenant_azurelocal_id: "00000000-aaaa-bbbb-cccc-111111111111" +``` + +--- + +## Integration with CI/CD + +### GitHub Actions Example + +```yaml +# .github/workflows/validate.yml +name: Validate Infrastructure + +on: [push, pull_request] + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Validate infrastructure.yml + run: | + npm install -g ajv-cli + ajv validate -s docs/standards/variable-management/assets/infrastructure.schema.json -d infrastructure.yml +``` + +### Azure DevOps Example + +```yaml +# azure-pipelines.yml +steps: + - task: PowerShell@2 + displayName: 'Validate Infrastructure Configuration' + inputs: + targetType: 'inline' + script: | + # Download schema + Invoke-WebRequest -Uri "$(Build.Repository.Uri)/docs/standards/variable-management/assets/infrastructure.schema.json" -OutFile schema.json + + # Validate + $config = Get-Content infrastructure.yml -Raw | ConvertFrom-Yaml + # Add validation logic here +``` + +--- + +## Advanced Validation + +### Custom Validators + +For complex validation beyond JSON Schema: + +```powershell +# Custom validation function +function Test-ClusterConfiguration { + param($config) + + # Cross-field validation + if ($config.compute.azure_local.cluster_node_count -gt 1) { + if (-not $config.networking.onprem.network_intents) { + throw "Multi-node clusters require storage network intent" + } + } +} +``` + +### Topology-Specific Validation + +```powershell +# Validate topology-specific constraints +switch ($config.compute.azure_local.network_topology) { + "switchless" { + if ($config.compute.azure_local.cluster_node_count -ne 2) { + throw "Switchless topology requires exactly 2 nodes" + } + } + "traditional" { + if ($config.compute.azure_local.cluster_node_count -lt 2) { + throw "Traditional topology requires minimum 2 nodes" + } + } +} +``` + +--- + +## Troubleshooting Validation + +### Schema Not Found + +**Error:** `Cannot find schema file` + +**Solution:** +```bash +# Ensure schema exists +ls docs/standards/variable-management/assets/infrastructure.schema.json + +# Regenerate if missing +generate-schema --input master-registry.yaml --output infrastructure.schema.json +``` + +### Invalid JSON + +**Error:** `Invalid JSON in schema file` + +**Solution:** +```bash +# Validate JSON syntax +python -m json.tool infrastructure.schema.json + +# Regenerate schema +generate-schema --input master-registry.yaml --output infrastructure.schema.json +``` + +### Version Mismatch + +**Error:** `Schema version 2.0.0 does not match registry version 2.1.0` + +**Solution:** +```bash +# Update schema to match registry +generate-schema --input master-registry.yaml --output infrastructure.schema.json --version 2.1.0 +``` + +--- + +## Downloads + +- [**📂 JSON Schema**](./assets/infrastructure.schema.json) +- [**📂 Master Registry**](./assets/master-registry.yaml) + +--- + +## Related Documents + +- [Registry Reference](./registry-reference.mdx) - Variable definitions +- [Usage Workflows](./usage-workflows.mdx) - Implementation guide +- [Examples](./examples.mdx) - Validation examples + +--- + +*Schema maintained by Product Technology team. Last updated: 2025-12-13* \ No newline at end of file diff --git a/standards/variable-management/technical-standards.mdx b/standards/variable-management/technical-standards.mdx new file mode 100644 index 000000000..c18ba4e20 --- /dev/null +++ b/standards/variable-management/technical-standards.mdx @@ -0,0 +1,763 @@ +--- +id: technical-standards +title: Technical Standards +sidebar_label: Technical Standards +--- + +# Technical Standards + +[![Standards](https://img.shields.io/badge/Type-Standards-blue?logo=yaml)](https://yaml.org/) +[![Technical](https://img.shields.io/badge/Level-Technical-purple)](https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/) +[![Implementation](https://img.shields.io/badge/Implementation-Details-green)](https://docs.microsoft.com/en-us/powershell/) + +> **Detailed technical specifications for variable naming, data types, validation patterns, and implementation standards** + +**Technical Reference** | **Implementation Standards** | **Detailed Specifications** + +--- + +## Overview + +This document provides the detailed technical specifications for implementing the variable registry system. It covers naming conventions, data type definitions, validation patterns, and implementation standards that ensure consistency across all repositories. + +### Technical Foundations + +The variable system is built on: + +- **YAML 1.2** - Human-readable configuration format +- **JSON Schema v7** - Validation specification +- **Semantic Versioning** - Registry versioning +- **Git** - Version control and distribution + +--- + +## Naming Conventions + +### Variable Naming Rules + +**Format:** `snake_case` (lowercase with underscores) + +**Pattern:** `^[a-z][a-z0-9_]*$` + +**Rules:** +- Start with lowercase letter +- Use only lowercase letters, numbers, underscores +- No spaces, hyphens, or special characters +- Maximum 50 characters +- Descriptive and unambiguous + +**Examples:** +```yaml +# Correct +cluster_name: "iic-clus01" +node_count: 4 +resource_group_name: "rg-iic-prodtech-eus-connectivity-hub" + +# Incorrect +clusterName: "iic-clus01" # camelCase +cluster-name: "iic-clus01" # hyphens +CLUSTER_NAME: "iic-clus01" # uppercase +cluster_name_too_long_for_netbios_compatibility: "iic-clus01" # too long +``` + +### Category Naming + +**Format:** `snake_case` (same as variables) + +**Examples:** +```yaml +azure_platform # Azure tenant, subscriptions, resource groups +identity # Accounts, AD, managed identities +networking # Azure / on-prem / hybrid networking +compute # VMs, clusters, nodes +``` + +### Environment-Specific Prefixes + +**Pattern:** `{environment_abbr}_{variable_name}` + +**Examples:** +```yaml +# Small 2-node deployment +iic_cluster_name: "iic-clus01" +iic_node_count: 2 + +# Larger 4-node deployment +lab_cluster_name: "lab-clus01" +lab_node_count: 4 +``` + +### Reserved Words + +**Avoid these variable names:** +- `name`, `type`, `id`, `key`, `value` (too generic) +- `config`, `settings`, `options` (too vague) +- `temp`, `tmp`, `test` (temporary/impermanent) +- `old`, `new`, `backup` (version-related) + +--- + +## Data Types + +### Primitive Types + +#### String + +**YAML Type:** `string` +**JSON Schema:** `"type": "string"` +**Description:** Text values, names, identifiers + +**Constraints:** +```yaml +cluster_name: + type: string + minLength: 1 + maxLength: 15 + pattern: "^[a-zA-Z][a-zA-Z0-9-]*$" +``` + +#### Integer + +**YAML Type:** `integer` +**JSON Schema:** `"type": "integer"` +**Description:** Whole numbers, counts, sizes + +**Constraints:** +```yaml +node_count: + type: integer + minimum: 1 + maximum: 16 + default: 2 +``` + +#### Boolean + +**YAML Type:** `boolean` +**JSON Schema:** `"type": "boolean"` +**Description:** True/false flags + +**Constraints:** +```yaml +gpu_enabled: + type: boolean + default: false +``` + +#### Array + +**YAML Type:** `array` (sequence) +**JSON Schema:** `"type": "array"` +**Description:** Lists of values + +**Constraints:** +```yaml +dns_servers: + type: array + items: + type: string + format: ipv4 + minItems: 1 + maxItems: 4 +``` + +#### Object + +**YAML Type:** `object` (mapping) +**JSON Schema:** `"type": "object"` +**Description:** Nested structures + +**Constraints:** +```yaml +network_intents: + type: object + properties: + management: + type: object + required: ["subnet_cidr", "gateway"] + required: ["management"] +``` + +### Specialized Formats + +#### UUID + +**Format:** `uuid` +**Pattern:** `^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$` + +```yaml +tenant_id: + type: string + format: uuid + example: "00000000-aaaa-bbbb-cccc-111111111111" +``` + +#### IPv4 Address + +**Format:** `ipv4` +**Pattern:** `^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$` + +```yaml +management_ip: + type: string + format: ipv4 + example: "192.168.1.10" +``` + +#### CIDR Notation + +**Format:** `cidr` +**Pattern:** `^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\/(?:[0-9]|[1-2][0-9]|3[0-2])$` + +```yaml +subnet_cidr: + type: string + format: cidr + example: "192.168.1.0/24" +``` + +#### MAC Address + +**Format:** `mac` +**Pattern:** `^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$` + +```yaml +node_mac_address: + type: string + format: mac + example: "AA:BB:CC:DD:EE:FF" +``` + +#### Email + +**Format:** `email` +**Pattern:** `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$` + +```yaml +contact_email: + type: string + format: email + example: "admin@improbability.cloud" +``` + +--- + +## Validation Patterns + +### Regex Patterns + +#### Cluster Name (NetBIOS Compatible) + +```yaml +cluster_name: + type: string + pattern: "^[a-zA-Z][a-zA-Z0-9-]*$" + maxLength: 15 + example: "iic-clus01" +``` + +#### Resource Group Name (CAF Compliant) + +```yaml +resource_group_name: + type: string + pattern: "^rg-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-(connectivity|identity|management|workload|azurelocal)-[a-z]+$" + example: "rg-iic-prodtech-eus-connectivity-hub" +``` + +#### Storage Account Name + +```yaml +storage_account_name: + type: string + pattern: "^[a-z0-9]{3,24}$" + example: "stiicprodtecheus001" +``` + +#### Key Vault Name + +```yaml +key_vault_name: + type: string + pattern: "^[a-zA-Z][a-zA-Z0-9-]{2,22}[a-zA-Z0-9]$" + example: "kv-iic-mgmt" +``` + +### Range Validation + +#### VLAN IDs + +```yaml +vlan_id: + type: integer + minimum: 0 + maximum: 4094 + example: 211 +``` + +#### Node Count + +```yaml +node_count: + type: integer + minimum: 1 + maximum: 16 + example: 4 +``` + +#### CPU Cores + +```yaml +cpu_cores: + type: integer + minimum: 4 + maximum: 128 + example: 32 +``` + +### Enum Validation + +#### Environment Types + +```yaml +environment_type: + type: string + enum: + - production + - development + - testing + - staging + example: "production" +``` + +#### Network Topologies + +```yaml +network_topology: + type: string + enum: + - switchless + - traditional + - nested + example: "traditional" +``` + +#### Azure Regions + +```yaml +azure_region: + type: string + enum: + - eastus + - eastus2 + - westus + - westus2 + - centralus + - northeurope + - westeurope + example: "eastus" +``` + +--- + +## Variable Organization + +### Hierarchical Structure + +**Registry Organization (v4.0.0 — 13 sections):** +```yaml +# 13 top-level sections +_metadata: # File version, change log +infrastructure_scenarios: # Deployment type taxonomy +site: # Physical location + hardware +environment: # Environment classification +tags: # Azure resource tags +azure_platform: # Tenants, subs, MGs, RGs + azure_tenants: ... + subscriptions: ... +identity: # Accounts, AD, MIs, B2B + accounts: ... + active_directory: ... +networking: # Azure / on-prem / hybrid + azure: + hub_spoke: ... + vnets: ... + onprem: + vlans: ... + network_intents: ... + hybrid: + vpn: ... +compute: # VMs, clusters, nodes, AVD + vms: + azure: ... + clusters: ... + cluster_nodes: ... +storage_accounts: # Azure Storage Accounts +security: # Key Vault, Defender, Sentinel + keyvault: ... + defender: ... +operations: # Monitoring, policy, BCDR + monitoring: ... + policy: ... +devops: # GitLab, Terraform + gitlab: ... + terraform: ... +``` + +### Category Definitions + +#### Infrastructure Scenarios + +Defines what infrastructure types are supported: + +```yaml +infrastructure_scenarios: + azure_local: + description: "Azure Local (Azure Stack HCI) clusters" + variable_groups: + - clusters + - cluster_nodes + - network_intents +``` + +#### Core Categories + +| Section | Sub-section | Purpose | Example Variables | +|---------|-------------|---------|-------------------| +| `_metadata` | — | File versioning | `version`, `last_updated` | +| `infrastructure_scenarios` | — | Deployment types | `azure_local`, `wsfc_s2d` | +| `site` | — | Physical location | `site_code`, `site_name` | +| `site` | `hardware` | Hardware details | `hw_vendor`, `hw_model` | +| `environment` | — | Environment metadata | `env_name`, `env_type` | +| `tags` | — | Azure resource tags | `Environment`, `Project` | +| `azure_platform` | `azure_tenants` | Azure identity | `aztenant_azurelocal_id` | +| `azure_platform` | `subscriptions` | Azure subscriptions | `sub_bootstrap_id` | +| `azure_platform` | `resource_groups` | Resource organization | `rg_connectivity_hub` | +| `identity` | `accounts` | Service accounts | `account_local_admin_*` | +| `identity` | `active_directory` | AD settings | `ad_domain_fqdn` | +| `networking` | `azure.vnets` | Virtual networks | `vnet_name` | +| `networking` | `onprem.vlans` | VLAN configs | `vlan_id`, `vlan_name` | +| `networking` | `hybrid.vpn` | VPN connectivity | `vpn_*` | +| `compute` | `azure_local` | Cluster definitions | `cluster_name`, `cluster_node_count` | +| `compute` | `cluster_nodes` | Node configs | `node_hostname`, `node_management_ip` | +| `compute` | `vms.azure` | Azure VMs | `azure_vm_dc1_*` | +| `storage_accounts` | — | Storage definitions | `storage_name`, `storage_tier` | +| `security` | `keyvault` | Key Vault | `kv_name`, `kv_uri` | +| `security` | `defender` | Defender for Cloud | Defender plans | +| `operations` | `monitoring` | Log Analytics, DCRs | Workspace config | +| `operations` | `policy` | Azure Policy | Policy assignments | +| `devops` | `gitlab` | GitLab CI/CD | `gitlab_project_id` | +| `devops` | `terraform` | Terraform state | `tf_backend_storage_account` | + +### Variable Relationships + +**Parent-Child Relationships:** +```yaml +# Parent variable (under compute section) +compute: + azure_local: + # Child variables + cluster_name: "iic-clus01" + cluster_node_count: 4 + # Related sibling section + cluster_nodes: + node01: + hostname: "iic-01-n01" + management_ip: "192.168.1.11" +``` + +**Reference Relationships:** +```yaml +# Variables can reference each other across sections +azure_platform: + resource_group_name: "rg-iic-prodtech-eus-landingzone-cluster" + +compute: + azure_local: + arc_resource_group: "rg-iic-prodtech-eus-landingzone-cluster" # References above +``` + +--- + +## Implementation Standards + +### File Structure + +#### infrastructure.yml + +**Location:** Repository root +**Purpose:** Environment-specific configuration values + +```yaml +# infrastructure.yml — follows v4.0.0 13-section hierarchy +version: "1.0.0" +lastUpdated: "2026-01-29" + +# Follow registry category structure +azure_platform: + tenant: + id: "00000000-aaaa-bbbb-cccc-111111111111" + name: "TierPoint Labs" + +compute: + azure_local: + cluster_name: "iic-clus01" + cluster_node_count: 4 + cluster_nodes: + node01: + hostname: "iic-01-n01" + management_ip: "192.168.1.11" +``` + +#### Registry Files + +**Location:** `variables/` subdirectory +**Purpose:** Self-contained validation + +``` +variables/ +├── master-registry.yaml # Registry copy +└── infrastructure.schema.json # Schema copy +``` + +### Script Integration Standards + +#### PowerShell Template + +```powershell +<# +.SYNOPSIS + Script using registry variables. + +.DESCRIPTION + Variables used from master registry: + - compute.azure_local.cluster_name (string, required, maxLength: 15) + - compute.azure_local.cluster_node_count (integer, required, 1-16) + +.PARAMETER ConfigPath + Path to infrastructure.yml conforming to infrastructure.schema.json + +.EXAMPLE + .\script.ps1 -ConfigPath .\infrastructure.yml +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory = $false)] + [ValidateScript({ Test-Path $_ })] + [string]$ConfigPath = (Join-Path $PSScriptRoot "../../configs/infrastructure.yml") +) + +# Load configuration +. "$PSScriptRoot/../utilities/helpers/config-loader.ps1" +$config = Get-InfrastructureConfig -ConfigPath $ConfigPath + +# Access registry variables +$clusterName = $config.compute.azure_local.cluster_name +$nodeCount = $config.compute.azure_local.cluster_node_count + +# Validate constraints (registry compliance) +if ($clusterName.Length -gt 15) { + throw "Cluster name exceeds registry maxLength of 15" +} + +if ($nodeCount -lt 1 -or $nodeCount -gt 16) { + throw "Node count outside registry range 1-16" +} +``` + +#### Python Template + +```python +#!/usr/bin/env python3 +""" +Script using registry variables. + +Variables used from master registry: +- compute.azure_local.cluster_name (string, required, maxLength: 15) +- compute.azure_local.cluster_node_count (integer, required, 1-16) +""" + +import yaml +import sys +from pathlib import Path + +def main(config_file: str): + # Load configuration + with open(config_file) as f: + config = yaml.safe_load(f) + + # Access registry variables + cluster_name = config['compute']['azure_local']['cluster_name'] + node_count = config['compute']['azure_local']['cluster_node_count'] + + # Validate constraints + if len(cluster_name) > 15: + raise ValueError("Cluster name exceeds registry maxLength of 15") + + if not (1 <= node_count <= 16): + raise ValueError("Node count outside registry range 1-16") + + print(f"Processing cluster: {cluster_name} with {node_count} nodes") + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Usage: script.py ") + sys.exit(1) + + main(sys.argv[1]) +``` + +### CI/CD Integration Standards + +#### GitHub Actions + +```yaml +# .github/workflows/validate.yml +name: Validate Infrastructure + +on: [push, pull_request] + +jobs: + validate: + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + + - name: Validate Configuration + run: | + # Download latest schema + Invoke-WebRequest -Uri "${{ github.server_url }}/${{ github.repository }}/blob/main/docs/standards/variable-management/assets/infrastructure.schema.json" ` + -OutFile variables/infrastructure.schema.json + + # Validate + .\tools\Validate-Infrastructure.ps1 -ConfigPath infrastructure.yml +``` + +#### Validation Script Standards + +```powershell +# Validate-Infrastructure.ps1 +[CmdletBinding()] +param( + [Parameter(Mandatory)] + [string]$ConfigFile +) + +# Load schema +$schemaPath = Join-Path $PSScriptRoot "variables/infrastructure.schema.json" +if (-not (Test-Path $schemaPath)) { + throw "Schema file not found: $schemaPath" +} + +$schema = Get-Content $schemaPath -Raw | ConvertFrom-Json + +# Load configuration +$config = Get-Content $ConfigFile -Raw | ConvertFrom-Yaml + +# Validate required fields +$missingFields = @() +foreach ($category in $schema.required) { + if (-not $config.$category) { + $missingFields += $category + } +} + +if ($missingFields) { + throw "Missing required categories: $($missingFields -join ', ')" +} + +# Validate data types and constraints +# [Implementation of detailed validation logic] + +Write-Host "[SUCCESS] Validation successful: $ConfigFile conforms to schema" +``` + +--- + +## Error Handling Standards + +### Validation Error Format + +**Standard Error Message:** +``` +[ERROR] Validation failed: 3 errors found + +Error 1: /compute/azure_local/cluster_node_count - Value 20 exceeds maximum of 16 + Location: infrastructure.yml:15 + Expected: integer ≤ 16 + Actual: 20 + +Error 2: /compute/azure_local/cluster_name - String length 24 exceeds maxLength of 15 + Location: infrastructure.yml:12 + Expected: string ≤ 15 characters + Actual: "this-is-a-very-long-name" (24 characters) + +Error 3: /azure_platform/tenant/id - Value does not match UUID pattern + Location: infrastructure.yml:5 + Expected: UUID format + Actual: "not-a-valid-uuid" +``` + +### Error Codes + +| Error Code | Description | Resolution | +|------------|-------------|------------| +| `VAL-001` | Missing required field | Add the missing field to infrastructure.yml | +| `VAL-002` | Type mismatch | Correct the data type to match registry | +| `VAL-003` | Pattern violation | Update value to match regex pattern | +| `VAL-004` | Range violation | Adjust value to fit min/max constraints | +| `VAL-005` | Enum violation | Use one of the allowed values | +| `VAL-006` | Format violation | Correct to valid format (UUID, IP, etc.) | + +--- + +## Performance Standards + +### Validation Performance + +**Target:** < 2 seconds for typical infrastructure.yml + +**Factors:** +- Registry size (currently ~910 variables) +- Configuration complexity +- JSON Schema processing overhead + +### Memory Usage + +**Target:** < 50MB RAM for validation process + +**Optimization:** +- Lazy loading of large schemas +- Streaming validation for large configs +- Cached compiled schemas + +### Scalability + +**Target:** Support 100+ environment repositories + +**Requirements:** +- Registry changes propagate within 24 hours +- Validation works offline (local schema copy) +- No single point of failure + +--- + +## Downloads + +- [**📂 Master Registry**](./assets/master-registry.yaml) +- [**📂 JSON Schema**](./assets/infrastructure.schema.json) + +--- + +## Related Documents + +- [Registry Reference](./registry-reference.mdx) - Variable definitions +- [Schema Validation](./schema-validation.mdx) - Validation guide +- [Usage Workflows](./usage-workflows.mdx) - Implementation guide +- [Governance](./governance.mdx) - Process management +- [Examples](./examples.mdx) - Usage examples + +--- + +*Technical standards maintained by Product Technology team. Last updated: 2026-01-29* \ No newline at end of file diff --git a/standards/variable-management/troubleshooting.mdx b/standards/variable-management/troubleshooting.mdx new file mode 100644 index 000000000..1c5589009 --- /dev/null +++ b/standards/variable-management/troubleshooting.mdx @@ -0,0 +1,486 @@ +--- +id: troubleshooting +title: Troubleshooting +sidebar_label: Troubleshooting +--- + +# Troubleshooting + +[![Troubleshooting](https://img.shields.io/badge/Type-Troubleshooting-red?logo=github)](https://github.com/features/troubleshooting) +[![Practical](https://img.shields.io/badge/Level-Practical-green)](https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/) +[![Operations](https://img.shields.io/badge/Operations-Diagnostics-orange)](https://docs.microsoft.com/en-us/powershell/) + +> **Common issues, error messages, and resolution steps for the variable registry and configuration system** + +**Diagnostics** | **Error Resolution** | **Common Pitfalls** + +--- + +## Overview + +This document covers common issues encountered when working with `infrastructure.yml`, the config-loader, YAML parsing, schema validation, and the variable registry system. Each section includes the error message, root cause, and resolution steps. + +--- + +## YAML Parsing Errors + +### Tab Characters in YAML + +**Error:** +``` +ConvertFrom-Yaml : while scanning for the next token +found character that cannot start any token +``` + +**Cause:** YAML does not allow tab characters for indentation. Only spaces are valid. + +**Resolution:** +```powershell +# Find tabs in your YAML file +(Get-Content .\configs\infrastructure.yml -Raw) -match "`t" + +# Replace tabs with spaces (2-space indent) +$content = Get-Content .\configs\infrastructure.yml -Raw +$content = $content -replace "`t", " " +Set-Content .\configs\infrastructure.yml -Value $content +``` + +**Prevention:** Configure your editor to insert spaces instead of tabs for `.yml`/`.yaml` files. + +### Special Characters in Strings + +**Error:** +``` +ConvertFrom-Yaml : while parsing a block mapping +did not find expected key +``` + +**Cause:** Unquoted strings containing YAML special characters (`:`, `#`, `{`, `}`, `[`, `]`, `&`, `*`, `!`, `|`, `>`). + +**Resolution:** +```yaml +# ❌ Wrong - colon in unquoted string +description: Deploy: Phase 1 + +# ✅ Correct - quoted string +description: "Deploy: Phase 1" +``` + +### Multiline String Issues + +**Error:** +``` +ConvertFrom-Yaml : while parsing a block scalar +found a tab character where an indentation space is expected +``` + +**Cause:** Inconsistent indentation in multiline strings. + +**Resolution:** +```yaml +# ❌ Wrong - mixed indentation +description: | + First line + Second line with extra indent + Third line with tab + +# ✅ Correct - consistent indentation +description: | + First line + Second line aligned + Third line aligned +``` + +--- + +## powershell-yaml Module Issues + +### Module Not Found + +**Error:** +``` +powershell-yaml module not found. Install with: Install-Module powershell-yaml -Scope CurrentUser +``` + +**Resolution:** +```powershell +# Install the module +Install-Module -Name powershell-yaml -Scope CurrentUser -Force + +# Verify installation +Get-Module -ListAvailable -Name powershell-yaml +``` + +### Version Conflicts + +**Error:** +``` +Import-Module : The following error occurred while loading the extended type data file: +``` + +**Cause:** Multiple versions of `powershell-yaml` installed. + +**Resolution:** +```powershell +# Check installed versions +Get-Module -ListAvailable -Name powershell-yaml | Select-Object Version, Path + +# Remove old versions +Get-Module -ListAvailable -Name powershell-yaml | + Sort-Object Version -Descending | + Select-Object -Skip 1 | + ForEach-Object { Uninstall-Module -Name powershell-yaml -RequiredVersion $_.Version } + +# Import specific version +Import-Module powershell-yaml -RequiredVersion 0.4.7 +``` + +### ConvertFrom-Yaml Returns Null + +**Error:** No error thrown, but `$config` is `$null` or empty. + +**Cause:** Empty YAML file or the `-Raw` flag missing from `Get-Content`. + +**Resolution:** +```powershell +# ❌ Wrong - Get-Content returns array of lines, not raw string +$config = Get-Content .\configs\infrastructure.yml | ConvertFrom-Yaml + +# ✅ Correct - use -Raw to get single string +$config = Get-Content .\configs\infrastructure.yml -Raw | ConvertFrom-Yaml +``` + +--- + +## Config-Loader Errors + +### Infrastructure Configuration Not Found + +**Error:** +``` +Get-InfrastructureConfig : Infrastructure configuration not found: C:\git\{repo}\configs\infrastructure.yml +``` + +**Cause:** The config-loader resolves the path relative to the script's repository root. If the script is running from an unexpected location, the path resolution fails. + +**Resolution:** +```powershell +# Option 1: Pass explicit path +$config = Get-InfrastructureConfig -ConfigPath "C:\git\my-repo\configs\infrastructure.yml" + +# Option 2: Verify current directory +Write-Host "Current directory: $(Get-Location)" +Write-Host "Config exists: $(Test-Path '.\configs\infrastructure.yml')" + +# Option 3: Check the repo root detection +# config-loader.ps1 uses $PSScriptRoot to find repo root +# Ensure the script is being run from within the repository structure +``` + +### Get-B2BConfig - Section Not Found + +**Error:** +``` +Get-B2BConfig : B2B configuration not found in infrastructure.yml. Ensure b2b_configuration section exists. +``` + +**Cause:** The `b2b_configuration` section is missing from `infrastructure.yml`. Not all environments require B2B configuration. + +**Resolution:** +```yaml +# Add the b2b_configuration section to infrastructure.yml +b2b_configuration: + service_tier: "standard" + management_tenant: + tenant_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + tenant_name: "Management Tenant" +``` + +### Ordered Dictionary Access Issues + +**Error:** +``` +Cannot index into a null array. +``` + +**Cause:** `ConvertFrom-Yaml` returns `OrderedDictionary` objects, not `PSCustomObject`. Property access syntax differs. + +**Resolution:** +```powershell +# ❌ Wrong - dot notation may fail on OrderedDictionary +$value = $config.compute.azure_local.cluster_name + +# ✅ Correct - use Get-ConfigValue from config-loader +$value = Get-ConfigValue -Config $config -Path "compute.azure_local.cluster_name" + +# ✅ Also correct - bracket notation +$value = $config["compute"]["azure_local"]["cluster_name"] +``` + +--- + +## Variable Path Contract Failures + +### Test-ConfigPaths Reports Missing Paths + +**Error:** +```powershell +$validation = Test-ConfigPaths -Config $config +# Returns: IsValid = False, MissingPaths = @("azure.tenant.id", "solution.name") +``` + +**Cause:** The configuration file is missing required paths defined in the Variable Path Contract. + +**Resolution:** +```powershell +# Check which paths are missing +$validation = Test-ConfigPaths -Config $config +if (-not $validation.IsValid) { + Write-Warning "Missing required configuration paths:" + foreach ($path in $validation.MissingPaths) { + Write-Warning " - $path" + } +} + +# Add the missing sections to infrastructure.yml +``` + +### Null vs Empty String Values + +**Error:** `Test-ConfigPaths` reports a path as missing even though it exists in the YAML. + +**Cause:** The path exists but has an empty value (`""` or `~`). + +**Resolution:** +```yaml +# ❌ These are treated as missing by Test-ConfigPaths +azure_platform: + tenant: + id: "" # Empty string + name: ~ # YAML null + +# ✅ Provide actual values +azure_platform: + tenant: + id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + name: "My Tenant" +``` + +--- + +## Schema Validation Failures + +### Required Field Missing + +**Error:** +``` +[ERROR] Validation failed: Missing required property: azure_platform + Location: infrastructure.yml (root) +``` + +**Resolution:** Add the missing section to your `infrastructure.yml`. Check the [Registry Reference](./registry-reference.mdx) for required fields and their structure. + +### Invalid Format (UUID, CIDR, MAC) + +**Error:** +``` +[ERROR] /azure_platform/tenant/id - Does not match format "uuid" + Expected: UUID format (e.g., "a1b2c3d4-e5f6-7890-abcd-ef1234567890") + Actual: "not-a-valid-uuid" +``` + +**Resolution:** +```yaml +# ❌ Invalid UUID format +azure_platform: + tenant: + id: "not-a-valid-uuid" + +# ✅ Valid UUID format (lowercase, with hyphens) +azure_platform: + tenant: + id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" +``` + +**Common format patterns:** +| Format | Pattern | Example | +|--------|---------|---------| +| UUID | `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` | `00000000-aaaa-bbbb-cccc-111111111111` | +| IPv4 | `x.x.x.x` | `10.42.1.11` | +| CIDR | `x.x.x.x/xx` | `10.42.1.0/24` | +| MAC | `xx:xx:xx:xx:xx:xx` | `00:1A:2B:3C:4D:5E` | + +### Node Count Mismatch + +**Error:** +``` +[ERROR] Node count mismatch: configured 4 nodes but defined 2 nodes +``` + +**Cause:** The `cluster_node_count` value doesn't match the number of entries in `cluster_nodes`. + +**Resolution:** +```yaml +# Ensure these match +compute: + azure_local: + cluster_node_count: 2 # Must match cluster_nodes count + cluster_nodes: # Must have exactly 2 entries + node01: + hostname: "iic-01-n01" + management_ip: "10.42.1.11" + node02: + hostname: "iic-01-n02" + management_ip: "10.42.1.12" +``` + +--- + +## Infrastructure.yml Merge Conflicts + +### YAML-Safe Conflict Resolution + +When Git produces merge conflicts in YAML files, the conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) break YAML parsing. + +**Error:** +``` +ConvertFrom-Yaml : while parsing a block mapping +did not find expected key +``` + +**Resolution:** +1. Open the file and resolve conflicts manually — YAML structure must be valid +2. Watch for broken indentation after removing conflict markers +3. Re-validate after resolution: + +```powershell +# Quick syntax check +try { + $config = Get-Content .\configs\infrastructure.yml -Raw | ConvertFrom-Yaml + Write-Host "YAML is valid" -ForegroundColor Green +} catch { + Write-Host "YAML is invalid: $($_.Exception.Message)" -ForegroundColor Red +} + +# Full schema validation +.\tools\Validate-Infrastructure.ps1 -ConfigPath .\configs\infrastructure.yml +``` + +### Duplicate Keys After Merge + +**Error:** +``` +ConvertFrom-Yaml : Duplicate key 'cluster_nodes' found +``` + +**Cause:** Both sides of the merge added content under the same YAML key. + +**Resolution:** Manually merge the two sections into one, ensuring no duplicate keys exist at the same level. + +--- + +## Common Pitfalls + +### Hardcoded Values in Scripts + +**Symptom:** Script works in one environment but fails in another. + +**Cause:** Values hardcoded instead of reading from `infrastructure.yml`. + +**Resolution:** Use `Get-InfrastructureConfig` from config-loader.ps1: +```powershell +# ❌ Wrong - hardcoded +$kvName = "kv-iic-platform" + +# ✅ Correct - from config +$config = Get-InfrastructureConfig -ConfigPath $ConfigFile +$kvName = $config.platform.kv_platform_name +``` + +### Wrong Config Path Hierarchy + +**Symptom:** Script reads a value but gets `$null`. + +**Cause:** YAML structure doesn't match the dot-notation path being used. + +**Resolution:** Use `Get-ConfigValue` with verbose output to trace the path: +```powershell +$config = Get-InfrastructureConfig -ConfigPath $ConfigFile + +# Trace what's available at each level +$config.Keys | ForEach-Object { Write-Host "Root key: $_" } +$config.azure_platform.Keys | ForEach-Object { Write-Host "Azure Platform key: $_" } + +# Use Get-ConfigValue for safe access +$value = Get-ConfigValue -Config $config -Path "azure_platform.tenant.id" +if ($null -eq $value) { + Write-Warning "Path 'azure_platform.tenant.id' not found in config" +} +``` + +### Key Vault Reference Format + +**Symptom:** Script tries to use `keyvault://...` string as a literal password. + +**Cause:** The `keyvault://` prefix is a reference URI, not a literal value. Scripts must resolve it. + +**Resolution:** +```powershell +$passwordRef = $config.identity.accounts.account_local_admin_password +# Value: "keyvault://kv-iic-platform/local-admin-password" + +if ($passwordRef -match '^keyvault://([^/]+)/(.+)$') { + $kvName = $Matches[1] # "kv-iic-platform" + $secretName = $Matches[2] # "local-admin-password" + $password = Get-AzKeyVaultSecret -VaultName $kvName -Name $secretName -AsPlainText +} +``` + +--- + +## Diagnostic Commands + +### Quick Health Check + +```powershell +# 1. Check YAML syntax +try { + $config = Get-Content .\configs\infrastructure.yml -Raw | ConvertFrom-Yaml + Write-Host "✅ YAML syntax valid" -ForegroundColor Green +} catch { + Write-Host "❌ YAML syntax error: $($_.Exception.Message)" -ForegroundColor Red + return +} + +# 2. Check required sections +$requiredSections = @("azure_platform", "compute", "networking", "identity", "security") +foreach ($section in $requiredSections) { + if ($config.ContainsKey($section)) { + Write-Host "✅ Section '$section' present" -ForegroundColor Green + } else { + Write-Host "❌ Section '$section' missing" -ForegroundColor Red + } +} + +# 3. Check node count consistency +$declaredCount = $config.compute.azure_local.cluster_node_count +$actualCount = $config.compute.cluster_nodes.Count +if ($declaredCount -eq $actualCount) { + Write-Host "✅ Node count matches: $declaredCount" -ForegroundColor Green +} else { + Write-Host "❌ Node count mismatch: declared=$declaredCount, defined=$actualCount" -ForegroundColor Red +} +``` + +--- + +## Related Documents + +- [Registry Reference](./registry-reference.mdx) - Variable definitions and constraints +- [Schema Validation](./schema-validation.mdx) - Validation rules and schema generation +- [Usage Workflows](./usage-workflows.mdx) - Implementation workflows +- [Examples](./examples.mdx) - Complete configuration examples + +--- + +*Troubleshooting guide maintained by Product Technology team. Last updated: 2026-02-08* diff --git a/standards/variable-management/usage-workflows.mdx b/standards/variable-management/usage-workflows.mdx new file mode 100644 index 000000000..32f1a8bae --- /dev/null +++ b/standards/variable-management/usage-workflows.mdx @@ -0,0 +1,541 @@ +--- +id: usage-workflows +title: Usage Workflows +sidebar_label: Usage Workflows +--- + +# Usage Workflows + +[![Workflows](https://img.shields.io/badge/Type-Workflows-blue?logo=githubactions)](https://github.com/features/actions) +[![Implementation](https://img.shields.io/badge/Implementation-Guide-green)](https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/) +[![Automation](https://img.shields.io/badge/Automation-Scripts-purple)](https://docs.microsoft.com/en-us/powershell/) + +> **How to implement variables across repositories - from setup to deployment** + +**Implementation Guide** | **Repository Integration** | **Step-by-Step Workflows** + +--- + +## Overview + +This guide explains how to implement the variable registry system across different repository types and use cases. It covers setup, integration, and deployment workflows for Product Technology environments. + +### Workflow Types + +1. **New Environment Setup** - Initialize a new repository with variables +2. **Existing Environment Migration** - Migrate legacy configurations +3. **Script Integration** - Use variables in PowerShell/Bash scripts +4. **IaC Integration** - Use variables in Terraform/Bicep/ARM templates +5. **CI/CD Integration** - Automate validation in pipelines + +--- + +## New Environment Setup + +### Task 1: Repository Initialization + +```powershell +# Clone environment template +git clone https://gitlab.com/tierpoint/prodtech/azurelocal/environments/template.git MyNewEnvironment +cd MyNewEnvironment + +# Copy master registry (for self-contained operation) +# Replace with the local path to the prodtech-documentation repository +Copy-Item -Path "\docs\standards\variable-management\assets\master-registry.yaml" -Destination ".\variables\" +Copy-Item -Path "\docs\standards\variable-management\assets\infrastructure.schema.json" -Destination ".\variables\" +``` + +### Task 2: Configure Environment Values + +```yaml +# infrastructure.yml - Environment-specific values (v4.0.0 13-section hierarchy) +version: "1.0.0" +lastUpdated: "2026-01-29" + +# Azure Platform (from registry: azure_platform) +azure_platform: + tenant: + id: "00000000-aaaa-bbbb-cccc-111111111111" + name: "Infinite Improbability Corp" + default_domain: "improbability.cloud" + region: "eastus" + resource_group_name: "rg-iic-prodtech-eus-landingzone-cluster" + +# Environment (from registry: environment) +environment: + name: "my-deployment" + type: "production" + owner: "infrastructure-team" + +# Compute (from registry: compute) +compute: + azure_local: + cluster_name: "iic-clus01" + cluster_node_count: 2 + arc_resource_group: "rg-iic-prodtech-eus-landingzone-cluster" + cluster_nodes: + node01: + hostname: "iic-01-n01" + management_ip: "10.0.0.11" + idrac_ip: "10.0.0.111" + service_tag: "ABC1234" + node02: + hostname: "iic-01-n02" + management_ip: "10.0.0.12" + idrac_ip: "10.0.0.112" + service_tag: "ABC1235" +``` + +### Task 3: Validate Configuration + +```powershell +# Validate against schema +.\tools\Validate-Infrastructure.ps1 -ConfigPath .\infrastructure.yml + +# Expected output: +# [SUCCESS] Validation successful: infrastructure.yml conforms to schema +``` + +### Task 4: Generate Documentation + +```powershell +# Generate cluster configuration documentation +.\tools\Generate-ClusterConfig.ps1 -ConfigPath .\infrastructure.yml -Output docs/cluster-configuration.md +``` + +--- + +## Script Integration + +### PowerShell Scripts + +**Template Structure:** +```powershell +<# +.SYNOPSIS + Deploy Azure Local cluster using registry variables. + +.DESCRIPTION + Reads infrastructure.yml and deploys cluster using variables defined in: + https://tierpoint-prodtech-documentation/docs/standards/variable-management/assets/master-registry.yaml + +.PARAMETER ConfigPath + Path to infrastructure.yml file containing cluster configuration. + File must conform to infrastructure.schema.json validation. + Defaults to configs/infrastructure.yml relative to the repository root. + +.EXAMPLE + .\Deploy-Cluster.ps1 -ConfigPath .\infrastructure.yml + +.NOTES + Variables used (from master registry): + - compute.azure_local.cluster_name (string, required, maxLength: 15) + - compute.azure_local.cluster_node_count (integer, required, range: 1-16) + - compute.cluster_nodes (object, required, 1-16 entries) + - networking.onprem.network_intents (array, required) + + Registry reference: + https://tierpoint-prodtech-documentation/docs/standards/variable-management/assets/master-registry.yaml +#> + +param( + [Parameter(Mandatory = $false)] + [ValidateScript({ Test-Path $_ })] + [string]$ConfigPath = (Join-Path $PSScriptRoot "../../configs/infrastructure.yml") +) + +# Load and parse YAML configuration +. "$PSScriptRoot/../utilities/helpers/config-loader.ps1" +$config = Get-InfrastructureConfig -ConfigPath $ConfigPath + +# Access registry-defined variables (v4.0.0 schema paths) +$clusterName = $config.compute.azure_local.cluster_name # compute.azure_local.cluster_name +$nodeCount = $config.compute.azure_local.cluster_node_count # compute.azure_local.cluster_node_count +$nodes = $config.compute.cluster_nodes # compute.cluster_nodes +$managementIntent = $config.networking.onprem.network_intents | Where-Object { $_.name -match 'Management' } | Select-Object -First 1 + +# Validate required fields (matches registry constraints) +if (-not $clusterName) { + throw "Required variable 'compute.azure_local.cluster_name' not found in configuration" +} + +if ($clusterName.Length -gt 15) { + throw "Variable 'compute.azure_local.cluster_name' exceeds max length of 15 characters" +} + +if ($nodeCount -lt 1 -or $nodeCount -gt 16) { + throw "Variable 'compute.azure_local.cluster_node_count' must be between 1-16" +} + +# Deploy cluster using registry variables +Write-Host "Deploying cluster: $clusterName with $nodeCount nodes" +foreach ($nodeName in $nodes.Keys) { + $node = $nodes[$nodeName] + Write-Host "Processing node: $($node.hostname) at $($node.management_ip)" +} +``` + +### Bash Scripts + +```bash +#!/bin/bash + +# Load configuration +CONFIG_FILE="infrastructure.yml" +if [ ! -f "$CONFIG_FILE" ]; then + echo "Error: $CONFIG_FILE not found" + exit 1 +fi + +# Parse YAML (requires yq or similar) — v4.0.0 schema paths +CLUSTER_NAME=$(yq '.compute.azure_local.cluster_name' "$CONFIG_FILE") +NODE_COUNT=$(yq '.compute.azure_local.cluster_node_count' "$CONFIG_FILE") + +# Validate constraints +if [ ${#CLUSTER_NAME} -gt 15 ]; then + echo "Error: Cluster name too long (max 15 chars)" + exit 1 +fi + +echo "Deploying cluster: $CLUSTER_NAME with $NODE_COUNT nodes" +``` + +--- + +## IaC Integration + +### Bicep Templates + +```bicep +// main.bicep +param config object + +// Access registry variables +var clusterName = config.compute.azure_local.cluster_name +var nodeCount = config.compute.azure_local.cluster_node_count +var resourceGroupName = config.azure_platform.resource_group_name + +resource clusterRG 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: config.azure_platform.region +} + +// Deploy cluster +module cluster 'cluster.bicep' = { + name: 'cluster-deployment' + params: { + clusterName: clusterName + nodeCount: nodeCount + nodes: config.compute.cluster_nodes + } +} +``` + +### Terraform + +```hcl +# main.tf +variable "config" { + type = object({ + compute = object({ + azure_local = object({ + cluster_name = string + cluster_node_count = number + arc_resource_group = string + }) + cluster_nodes = map(object({ + hostname = string + management_ip = string + })) + }) + azure_platform = object({ + resource_group_name = string + }) + }) + validation { + condition = length(var.config.compute.azure_local.cluster_name) <= 15 + error_message = "Cluster name must be 15 characters or less" + } +} + +resource "azurerm_resource_group" "cluster" { + name = var.config.azure_platform.resource_group_name + location = "East US" +} + +# Deploy cluster nodes +resource "azurerm_windows_virtual_machine" "nodes" { + for_each = var.config.compute.cluster_nodes + + name = each.value.hostname + resource_group_name = azurerm_resource_group.cluster.name + location = azurerm_resource_group.cluster.location + size = "Standard_D8s_v3" + admin_username = "localadmin" + admin_password = data.azurerm_key_vault_secret.admin_password.value + + network_interface_ids = [ + azurerm_network_interface.nodes[each.key].id, + ] +} +``` + +### ARM Templates + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "config": { + "type": "object", + "metadata": { + "description": "Infrastructure configuration from registry" + } + } + }, + "variables": { + "clusterName": "[parameters('config').compute.azure_local.cluster_name]", + "nodeCount": "[parameters('config').compute.azure_local.cluster_node_count]" + }, + "resources": [ + { + "type": "Microsoft.Resources/resourceGroups", + "apiVersion": "2021-04-01", + "name": "[parameters('config').azure_platform.resource_group_name]", + "location": "[parameters('config').azure_platform.region]" + } + ] +} +``` + +--- + +## CI/CD Integration + +### GitHub Actions + +```yaml +# .github/workflows/deploy.yml +name: Deploy Infrastructure + +on: + push: + branches: [ main ] + +jobs: + validate-and-deploy: + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup PowerShell + uses: microsoft/setup-powershell@v1 + + - name: Validate Configuration + run: | + .\tools\Validate-Infrastructure.ps1 -ConfigPath .\infrastructure.yml + + - name: Deploy Infrastructure + run: | + .\tools\Deploy-Infrastructure.ps1 -ConfigPath .\infrastructure.yml + env: + AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} +``` + +### GitLab CI + +```yaml +# .gitlab-ci.yml +stages: + - validate + - deploy + +validate: + stage: validate + script: + - pwsh -Command ".\tools\Validate-Infrastructure.ps1 -ConfigPath .\infrastructure.yml" + only: + - merge_requests + +deploy: + stage: deploy + script: + - pwsh -Command ".\tools\Deploy-Infrastructure.ps1 -ConfigPath .\infrastructure.yml" + environment: + name: production + only: + - main +``` + +### Azure DevOps + +```yaml +# azure-pipelines.yml +trigger: + - main + +pool: + vmImage: 'windows-latest' + +steps: + - task: PowerShell@2 + displayName: 'Validate Infrastructure Configuration' + inputs: + targetType: 'inline' + script: | + .\tools\Validate-Infrastructure.ps1 -ConfigPath .\infrastructure.yml + + - task: PowerShell@2 + displayName: 'Deploy Infrastructure' + inputs: + targetType: 'inline' + script: | + .\tools\Deploy-Infrastructure.ps1 -ConfigPath .\infrastructure.yml + env: + AZURE_CREDENTIALS: $(AZURE_CREDENTIALS) +``` + +--- + +## Environment-Specific Workflows + +### Small Cluster (2-Node Switchless) + +**Constraints:** +- Exactly 2 nodes +- Switchless topology +- Limited VLAN support + +```yaml +# infrastructure.yml — 2-node switchless example +compute: + azure_local: + cluster_name: "iic-clus01" + cluster_node_count: 2 + network_topology: "switchless" + cluster_nodes: + node01: + hostname: "iic-01-n01" + management_ip: "10.0.0.11" + node02: + hostname: "iic-01-n02" + management_ip: "10.0.0.12" +``` + +### Large Cluster (4-Node with GPU) + +**Requirements:** +- 4 nodes +- GPU-enabled +- Traditional switched topology + +```yaml +# infrastructure.yml — 4-node GPU-enabled example +compute: + azure_local: + cluster_name: "iic-clus02" + cluster_node_count: 4 + network_topology: "switched" + +site: + hardware: + vendor: "Dell" + model: "AX-760" + gpu_enabled: true + gpu_model: "NVIDIA-L4" +``` + +### Proof of Concept (Nested Virtualization) + +**Characteristics:** +- 3-4 nodes +- Nested Hyper-V +- Validate-only deployment mode + +```yaml +# infrastructure.yml — nested/PoC example +compute: + azure_local: + cluster_name: "poc-clus01" + cluster_node_count: 3 + network_topology: "nested" + deployment_mode: "validate_only" +``` + +--- + +## Migration Workflows + +### Migrating Legacy Configurations + +**Task 1: Analyze Existing Config** +```powershell +# Extract variables from existing scripts/configs +Get-ChildItem *.ps1 -Recurse | Select-String '\$.*=' | Out-File variables.txt +``` + +**Task 2: Map to Registry Variables** +```powershell +# Create mapping document +$mapping = @{ + '$clusterName' = 'compute.azure_local.cluster_name' + '$nodeCount' = 'compute.azure_local.cluster_node_count' + '$resourceGroup' = 'azure_platform.resource_group_name' +} +$mapping | ConvertTo-Json | Out-File variable-mapping.json +``` + +**Task 3: Update Scripts** +```powershell +# Replace hardcoded variables with config references +$config = Get-Content .\infrastructure.yml -Raw | ConvertFrom-Yaml +$clusterName = $config.compute.azure_local.cluster_name # Instead of $clusterName = "my-cluster" +``` + +**Task 4: Validate Migration** +```powershell +# Ensure all variables are now registry-compliant +.\tools\Validate-Infrastructure.ps1 -ConfigPath .\infrastructure.yml +.\tools\Validate-Scripts.ps1 -ScriptPath .\Deploy-Cluster.ps1 +``` + +--- + +## Best Practices + +### Development Workflow + +1. **Always validate before commit** +2. **Use registry variable names exactly** +3. **Document variable usage in script headers** +4. **Test with multiple environments** +5. **Keep infrastructure.yml in sync** + +### Production Deployment + +1. **Validate in CI/CD pipeline** +2. **Use immutable infrastructure patterns** +3. **Store secrets in Key Vault** +4. **Monitor deployment metrics** +5. **Maintain deployment logs** + +--- + +## Downloads + +- [**📂 Master Registry**](./assets/master-registry.yaml) +- [**📂 JSON Schema**](./assets/infrastructure.schema.json) + +--- + +## Related Documents + +- [Registry Reference](./registry-reference.mdx) - Variable definitions +- [Schema Validation](./schema-validation.mdx) - Validation guide +- [Governance](./governance.mdx) - Process management +- [Examples](./examples.mdx) - Implementation examples + +--- + +*Workflows maintained by Product Technology team. Last updated: 2026-01-29* \ No newline at end of file