diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index a721d0b..94aa61e 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -32,3 +32,11 @@ Closes # - [ ] My code follows the project's style guidelines in `CONTRIBUTING.md` - [ ] My commit messages follow [Conventional Commits](https://www.conventionalcommits.org/) - [ ] I have not committed any log files, CSV exports, or VHDX backups + +## 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-config.yml b/.github/workflows/validate-config.yml new file mode 100644 index 0000000..538f0ef --- /dev/null +++ b/.github/workflows/validate-config.yml @@ -0,0 +1,60 @@ +# ============================================================================= +# validate-config.yml — Validate config/variables.example.yml against schema +# ============================================================================= +# Triggered on PRs and pushes that touch config/ or this workflow. +# Validates YAML syntax and JSON Schema compliance. +# ============================================================================= + +name: Validate Configuration + +on: + push: + branches: [main] + paths: + - 'config/**' + - '.github/workflows/validate-config.yml' + pull_request: + branches: [main] + paths: + - 'config/**' + workflow_dispatch: + +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install dependencies + run: pip install pyyaml jsonschema + + - name: Validate variables.example.yml against schema + run: | + python3 -c " + import yaml, json, sys + from jsonschema import validate, ValidationError + + with open('config/variables.example.yml') as f: + data = yaml.safe_load(f) + + with open('config/schema/variables.schema.json') as f: + schema = json.load(f) + + try: + validate(instance=data, schema=schema) + print('✅ config/variables.example.yml passes schema validation') + except ValidationError as e: + print(f'❌ Schema validation failed: {e.message}') + print(f' Path: {\" > \".join(str(p) for p in e.absolute_path)}') + sys.exit(1) + " diff --git a/.github/workflows/validate-repo-structure.yml b/.github/workflows/validate-repo-structure.yml new file mode 100644 index 0000000..e50c5a7 --- /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 4efa23a..1d0c399 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ Backups/ Temp/ Configs/ +# User-specific config (actual values — never commit) +config/variables.yml + # Log and report files *.log *.csv diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8ff4060..a08b6d8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,6 +48,16 @@ Use the [feature request issue template](.github/ISSUE_TEMPLATE/feature_request. - At minimum, run `.\01-Setup-ConversionEnvironment.ps1` against a real cluster to validate your changes don't break inventory/export - Test `.\02-Convert-MBRtoGPT.ps1 -ValidateOnly` inside a guest VM before testing the full conversion +## 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 +- [Scripting Standards](https://azurelocal.cloud/standards/scripting/scripting-standards) — PowerShell conventions +- [Documentation Standards](https://azurelocal.cloud/standards/documentation/documentation-standards) — Writing and formatting +- [Variable Management](https://azurelocal.cloud/docs/implementation/04-variable-management-standard) — Config file patterns +- [Fictional Company Policy](https://azurelocal.cloud/standards/fictional-company-policy) — Use IIC, never Contoso + ## Code of Conduct Be respectful and constructive. This is a small project focused on solving a real operational problem — keep discussions on-topic and collaborative. diff --git a/config/schema/variables.schema.json b/config/schema/variables.schema.json new file mode 100644 index 0000000..15369ea --- /dev/null +++ b/config/schema/variables.schema.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://github.com/AzureLocal/azurelocal-vm-conversion-toolkit/config/schema/variables.schema.json", + "title": "VM Conversion Toolkit Variables", + "description": "Schema for config/variables.example.yml — validates required sections and key structure.", + "type": "object", + "required": ["azure", "azure_local", "conversion", "tags"], + "properties": { + "azure": { + "type": "object", + "required": ["subscription_id", "resource_group", "location"], + "properties": { + "subscription_id": { "type": "string" }, + "resource_group": { "type": "string" }, + "location": { "type": "string" } + } + }, + "azure_local": { + "type": "object", + "required": ["custom_location_id", "logical_network_id"], + "properties": { + "custom_location_id": { "type": "string" }, + "logical_network_id": { "type": "string" } + } + }, + "conversion": { + "type": "object", + "required": ["working_directory"], + "properties": { + "working_directory": { "type": "string" }, + "max_parallel": { "type": "integer", "minimum": 1 } + } + }, + "tags": { + "type": "object", + "additionalProperties": { "type": "string" } + } + }, + "additionalProperties": false +} diff --git a/config/variables.example.yml b/config/variables.example.yml new file mode 100644 index 0000000..86f7538 --- /dev/null +++ b/config/variables.example.yml @@ -0,0 +1,48 @@ +# ============================================================================= +# variables.example.yml +# Central variable reference for VM Gen1-to-Gen2 conversion scripts. +# +# Copy this file to variables.yml and fill in your values: +# cp config/variables.example.yml config/variables.yml +# +# DO NOT commit variables.yml — it is excluded by .gitignore. +# +# NOTE: These scripts currently accept parameters directly on the command line. +# This file documents the common values you will need across all scripts. +# Future versions may support loading from this file directly. +# ============================================================================= + + +# ============================================================================= +# Azure +# ============================================================================= +azure: + subscription_id: "00000000-0000-0000-0000-000000000000" + resource_group: "rg-azurelocal-prod" + location: "eastus" + + +# ============================================================================= +# Azure Local +# ============================================================================= +azure_local: + custom_location_id: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-azurelocal/providers/Microsoft.ExtendedLocation/customLocations/cl-azurelocal-01" + logical_network_id: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-azurelocal/providers/Microsoft.AzureStackHCI/logicalNetworks/lnet-mgmt-01" + + +# ============================================================================= +# Conversion Settings +# ============================================================================= +conversion: + working_directory: "C:\\ClusterStorage\\Volume01\\Gen2Conversion" + max_parallel: 1 # VMs to process in parallel (1 = sequential) + + +# ============================================================================= +# Tags +# ============================================================================= +tags: + project: "VM-Conversion" + environment: "production" + workload: "gen1-to-gen2" + solution: "vmconvert-azure-local" diff --git a/docs/reference/variables.md b/docs/reference/variables.md new file mode 100644 index 0000000..1026791 --- /dev/null +++ b/docs/reference/variables.md @@ -0,0 +1,105 @@ +# Variable Reference + +All VM conversion scripts use a central configuration file: `config/variables.yml`. This file documents the common values you will need across all scripts. Future versions may support loading from this file directly. + +!!! tip "Getting started" + Copy the example and fill in your values: + ```powershell + cp config/variables.example.yml config/variables.yml + ``` + **Never commit** `variables.yml` — it is excluded by `.gitignore` because it contains environment-specific values. + +!!! note "Current usage" + These scripts currently accept parameters directly on the command line. + This file documents the common values you will need and serves as the canonical parameter reference. + +--- + +## Naming Rules + +| Scope | Convention | Example | +|-------|-----------|---------| +| Top-level sections | `snake_case` | `azure_local`, `conversion` | +| Keys within sections | `snake_case` | `subscription_id`, `max_parallel` | +| Azure resource IDs | Full ARM resource ID | `/subscriptions/.../customLocations/cl-01` | + +--- + +## Azure + +```yaml +azure: + subscription_id: "00000000-0000-0000-0000-000000000000" + resource_group: "rg-azurelocal-prod" + location: "eastus" +``` + +| Variable | Type | Required | Description | Default | +|----------|------|:--------:|-------------|---------| +| `azure.subscription_id` | string | **Yes** | Azure subscription ID | — | +| `azure.resource_group` | string | **Yes** | Resource group containing the target VMs | — | +| `azure.location` | string | **Yes** | Azure region | `eastus` | + +--- + +## Azure Local + +```yaml +azure_local: + custom_location_id: "/subscriptions/.../customLocations/cl-azurelocal-01" + logical_network_id: "/subscriptions/.../logicalNetworks/lnet-mgmt-01" +``` + +| Variable | Type | Required | Description | Default | +|----------|------|:--------:|-------------|---------| +| `azure_local.custom_location_id` | string | **Yes** | Full ARM resource ID of the Azure Local custom location | — | +| `azure_local.logical_network_id` | string | **Yes** | Full ARM resource ID of the logical network for the converted VM | — | + +--- + +## Conversion Settings + +```yaml +conversion: + working_directory: "C:\\ClusterStorage\\Volume01\\Gen2Conversion" + max_parallel: 1 +``` + +| Variable | Type | Required | Description | Default | +|----------|------|:--------:|-------------|---------| +| `conversion.working_directory` | string | **Yes** | Scratch directory for conversion work files (must have sufficient free space) | — | +| `conversion.max_parallel` | integer | No | Number of VMs to process in parallel (`1` = sequential) | `1` | + +--- + +## Tags + +```yaml +tags: + project: "VM-Conversion" + environment: "production" + workload: "gen1-to-gen2" + solution: "vmconvert-azure-local" +``` + +| Variable | Type | Required | Description | Default | +|----------|------|:--------:|-------------|---------| +| `tags.project` | string | No | Project tag | `VM-Conversion` | +| `tags.environment` | string | No | Environment tag | `production` | +| `tags.workload` | string | No | Workload type tag | `gen1-to-gen2` | +| `tags.solution` | string | No | Solution identifier tag | `vmconvert-azure-local` | + +--- + +## Script Parameter Mapping + +The table below maps `variables.yml` keys to the actual script parameters: + +| Variable Key | Azure Local Script Param | Hyper-V Script Param | +|-------------|-------------------------|---------------------| +| `azure.subscription_id` | `-SubscriptionId` | N/A | +| `azure.resource_group` | `-ResourceGroup` | N/A | +| `azure_local.custom_location_id` | `-CustomLocationId` | N/A | +| `azure_local.logical_network_id` | `-LogicalNetworkId` | N/A | +| `conversion.working_directory` | `-WorkingDirectory` | `-WorkingDirectory` | +| `conversion.max_parallel` | `-MaxParallel` | `-MaxParallel` | diff --git a/mkdocs.yml b/mkdocs.yml index fec6eb6..d2a1f15 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -40,6 +40,8 @@ nav: - Azure Local Path: runbook-azurelocal.md - Hyper-V Path: runbook-hyperv.md - Troubleshooting: troubleshooting.md + - Reference: + - Variable Reference: reference/variables.md - Contributing: contributing.md markdown_extensions: