Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,11 @@ Closes #<!-- issue number -->
- [ ] 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
60 changes: 60 additions & 0 deletions .github/workflows/validate-config.yml
Original file line number Diff line number Diff line change
@@ -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)
"
80 changes: 80 additions & 0 deletions .github/workflows/validate-repo-structure.yml
Original file line number Diff line number Diff line change
@@ -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"
Comment on lines +44 to +45
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ Backups/
Temp/
Configs/

# User-specific config (actual values — never commit)
config/variables.yml

# Log and report files
*.log
*.csv
Expand Down
10 changes: 10 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
40 changes: 40 additions & 0 deletions config/schema/variables.schema.json
Original file line number Diff line number Diff line change
@@ -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
}
48 changes: 48 additions & 0 deletions config/variables.example.yml
Original file line number Diff line number Diff line change
@@ -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"
105 changes: 105 additions & 0 deletions docs/reference/variables.md
Original file line number Diff line number Diff line change
@@ -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` |
2 changes: 2 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading