Skip to content

Commit dd3cb21

Browse files
Merge pull request #4 from AzureLocal/feature/issue-14-unified-standards
[INFRA] Establish unified project-wide standards (issue #14)
2 parents e9a927d + 3834d04 commit dd3cb21

9 files changed

Lines changed: 356 additions & 0 deletions

File tree

.github/pull_request_template.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,11 @@ Closes #<!-- issue number -->
3232
- [ ] My code follows the project's style guidelines in `CONTRIBUTING.md`
3333
- [ ] My commit messages follow [Conventional Commits](https://www.conventionalcommits.org/)
3434
- [ ] I have not committed any log files, CSV exports, or VHDX backups
35+
36+
## Standards Compliance
37+
38+
- [ ] Follows [repo structure standard](https://azurelocal.cloud/standards/repo-structure) (required files present)
39+
- [ ] Follows [naming conventions](https://azurelocal.cloud/standards/documentation/naming-conventions) (files, variables, resources)
40+
- [ ] Uses [IIC fictional company](https://azurelocal.cloud/standards/fictional-company-policy) in all examples (never Contoso)
41+
- [ ] Config changes validated against JSON Schema (if applicable)
42+
- [ ] No hardcoded IPs, names, secrets, or environment-specific values in committed code
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# =============================================================================
2+
# validate-config.yml — Validate config/variables.example.yml against schema
3+
# =============================================================================
4+
# Triggered on PRs and pushes that touch config/ or this workflow.
5+
# Validates YAML syntax and JSON Schema compliance.
6+
# =============================================================================
7+
8+
name: Validate Configuration
9+
10+
on:
11+
push:
12+
branches: [main]
13+
paths:
14+
- 'config/**'
15+
- '.github/workflows/validate-config.yml'
16+
pull_request:
17+
branches: [main]
18+
paths:
19+
- 'config/**'
20+
workflow_dispatch:
21+
22+
permissions:
23+
contents: read
24+
25+
jobs:
26+
validate:
27+
runs-on: ubuntu-latest
28+
29+
steps:
30+
- name: Checkout repository
31+
uses: actions/checkout@v4
32+
33+
- name: Setup Python
34+
uses: actions/setup-python@v5
35+
with:
36+
python-version: '3.12'
37+
38+
- name: Install dependencies
39+
run: pip install pyyaml jsonschema
40+
41+
- name: Validate variables.example.yml against schema
42+
run: |
43+
python3 -c "
44+
import yaml, json, sys
45+
from jsonschema import validate, ValidationError
46+
47+
with open('config/variables.example.yml') as f:
48+
data = yaml.safe_load(f)
49+
50+
with open('config/schema/variables.schema.json') as f:
51+
schema = json.load(f)
52+
53+
try:
54+
validate(instance=data, schema=schema)
55+
print('✅ config/variables.example.yml passes schema validation')
56+
except ValidationError as e:
57+
print(f'❌ Schema validation failed: {e.message}')
58+
print(f' Path: {\" > \".join(str(p) for p in e.absolute_path)}')
59+
sys.exit(1)
60+
"
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
name: Validate Repo Structure
2+
on:
3+
pull_request:
4+
branches: [main]
5+
6+
jobs:
7+
check-structure:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v4
11+
12+
- name: Check required root files
13+
run: |
14+
missing=0
15+
for f in README.md CONTRIBUTING.md LICENSE CHANGELOG.md .gitignore; do
16+
if [ ! -f "$f" ]; then
17+
echo "::error::Missing required file: $f"
18+
missing=$((missing + 1))
19+
fi
20+
done
21+
if [ $missing -gt 0 ]; then
22+
echo "::error::$missing required root file(s) missing"
23+
exit 1
24+
fi
25+
echo "All required root files present"
26+
27+
- name: Check required directories
28+
run: |
29+
missing=0
30+
for d in docs .github; do
31+
if [ ! -d "$d" ]; then
32+
echo "::error::Missing required directory: $d/"
33+
missing=$((missing + 1))
34+
fi
35+
done
36+
if [ $missing -gt 0 ]; then
37+
echo "::error::$missing required directory(s) missing"
38+
exit 1
39+
fi
40+
echo "All required directories present"
41+
42+
- name: Check PR template
43+
run: |
44+
if [ ! -f ".github/PULL_REQUEST_TEMPLATE.md" ]; then
45+
echo "::error::Missing .github/PULL_REQUEST_TEMPLATE.md"
46+
exit 1
47+
fi
48+
echo "PR template found"
49+
50+
- name: Check config structure (if config dir exists)
51+
run: |
52+
if [ -d "config" ]; then
53+
missing=0
54+
if [ ! -f "config/variables.example.yml" ]; then
55+
echo "::error::Missing config/variables.example.yml"
56+
missing=$((missing + 1))
57+
fi
58+
if [ ! -f "config/schema/variables.schema.json" ]; then
59+
echo "::error::Missing config/schema/variables.schema.json"
60+
missing=$((missing + 1))
61+
fi
62+
if [ $missing -gt 0 ]; then
63+
exit 1
64+
fi
65+
echo "Config structure valid"
66+
else
67+
echo "No config/ directory — skipping config checks"
68+
fi
69+
70+
- name: Check variable reference doc (if config dir exists)
71+
run: |
72+
if [ -d "config" ]; then
73+
if [ ! -f "docs/reference/variables.md" ]; then
74+
echo "::error::Missing docs/reference/variables.md (required when config/ exists)"
75+
exit 1
76+
fi
77+
echo "Variable reference doc found"
78+
else
79+
echo "No config/ directory — skipping variable reference check"
80+
fi

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ Backups/
44
Temp/
55
Configs/
66

7+
# User-specific config (actual values — never commit)
8+
config/variables.yml
9+
710
# Log and report files
811
*.log
912
*.csv

CONTRIBUTING.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ Use the [feature request issue template](.github/ISSUE_TEMPLATE/feature_request.
4848
- At minimum, run `.\01-Setup-ConversionEnvironment.ps1` against a real cluster to validate your changes don't break inventory/export
4949
- Test `.\02-Convert-MBRtoGPT.ps1 -ValidateOnly` inside a guest VM before testing the full conversion
5050

51+
## Standards
52+
53+
This project follows the **org-wide AzureLocal standards** documented at [azurelocal.cloud/standards](https://azurelocal.cloud/standards/). Key references:
54+
55+
- [Repository Structure](https://azurelocal.cloud/standards/repo-structure) — Required files, directories, labels, branch naming
56+
- [Scripting Standards](https://azurelocal.cloud/standards/scripting/scripting-standards) — PowerShell conventions
57+
- [Documentation Standards](https://azurelocal.cloud/standards/documentation/documentation-standards) — Writing and formatting
58+
- [Variable Management](https://azurelocal.cloud/docs/implementation/04-variable-management-standard) — Config file patterns
59+
- [Fictional Company Policy](https://azurelocal.cloud/standards/fictional-company-policy) — Use IIC, never Contoso
60+
5161
## Code of Conduct
5262

5363
Be respectful and constructive. This is a small project focused on solving a real operational problem — keep discussions on-topic and collaborative.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$id": "https://github.com/AzureLocal/azurelocal-vm-conversion-toolkit/config/schema/variables.schema.json",
4+
"title": "VM Conversion Toolkit Variables",
5+
"description": "Schema for config/variables.example.yml — validates required sections and key structure.",
6+
"type": "object",
7+
"required": ["azure", "azure_local", "conversion", "tags"],
8+
"properties": {
9+
"azure": {
10+
"type": "object",
11+
"required": ["subscription_id", "resource_group", "location"],
12+
"properties": {
13+
"subscription_id": { "type": "string" },
14+
"resource_group": { "type": "string" },
15+
"location": { "type": "string" }
16+
}
17+
},
18+
"azure_local": {
19+
"type": "object",
20+
"required": ["custom_location_id", "logical_network_id"],
21+
"properties": {
22+
"custom_location_id": { "type": "string" },
23+
"logical_network_id": { "type": "string" }
24+
}
25+
},
26+
"conversion": {
27+
"type": "object",
28+
"required": ["working_directory"],
29+
"properties": {
30+
"working_directory": { "type": "string" },
31+
"max_parallel": { "type": "integer", "minimum": 1 }
32+
}
33+
},
34+
"tags": {
35+
"type": "object",
36+
"additionalProperties": { "type": "string" }
37+
}
38+
},
39+
"additionalProperties": false
40+
}

config/variables.example.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# =============================================================================
2+
# variables.example.yml
3+
# Central variable reference for VM Gen1-to-Gen2 conversion scripts.
4+
#
5+
# Copy this file to variables.yml and fill in your values:
6+
# cp config/variables.example.yml config/variables.yml
7+
#
8+
# DO NOT commit variables.yml — it is excluded by .gitignore.
9+
#
10+
# NOTE: These scripts currently accept parameters directly on the command line.
11+
# This file documents the common values you will need across all scripts.
12+
# Future versions may support loading from this file directly.
13+
# =============================================================================
14+
15+
16+
# =============================================================================
17+
# Azure
18+
# =============================================================================
19+
azure:
20+
subscription_id: "00000000-0000-0000-0000-000000000000"
21+
resource_group: "rg-azurelocal-prod"
22+
location: "eastus"
23+
24+
25+
# =============================================================================
26+
# Azure Local
27+
# =============================================================================
28+
azure_local:
29+
custom_location_id: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-azurelocal/providers/Microsoft.ExtendedLocation/customLocations/cl-azurelocal-01"
30+
logical_network_id: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-azurelocal/providers/Microsoft.AzureStackHCI/logicalNetworks/lnet-mgmt-01"
31+
32+
33+
# =============================================================================
34+
# Conversion Settings
35+
# =============================================================================
36+
conversion:
37+
working_directory: "C:\\ClusterStorage\\Volume01\\Gen2Conversion"
38+
max_parallel: 1 # VMs to process in parallel (1 = sequential)
39+
40+
41+
# =============================================================================
42+
# Tags
43+
# =============================================================================
44+
tags:
45+
project: "VM-Conversion"
46+
environment: "production"
47+
workload: "gen1-to-gen2"
48+
solution: "vmconvert-azure-local"

docs/reference/variables.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Variable Reference
2+
3+
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.
4+
5+
!!! tip "Getting started"
6+
Copy the example and fill in your values:
7+
```powershell
8+
cp config/variables.example.yml config/variables.yml
9+
```
10+
**Never commit** `variables.yml` — it is excluded by `.gitignore` because it contains environment-specific values.
11+
12+
!!! note "Current usage"
13+
These scripts currently accept parameters directly on the command line.
14+
This file documents the common values you will need and serves as the canonical parameter reference.
15+
16+
---
17+
18+
## Naming Rules
19+
20+
| Scope | Convention | Example |
21+
|-------|-----------|---------|
22+
| Top-level sections | `snake_case` | `azure_local`, `conversion` |
23+
| Keys within sections | `snake_case` | `subscription_id`, `max_parallel` |
24+
| Azure resource IDs | Full ARM resource ID | `/subscriptions/.../customLocations/cl-01` |
25+
26+
---
27+
28+
## Azure
29+
30+
```yaml
31+
azure:
32+
subscription_id: "00000000-0000-0000-0000-000000000000"
33+
resource_group: "rg-azurelocal-prod"
34+
location: "eastus"
35+
```
36+
37+
| Variable | Type | Required | Description | Default |
38+
|----------|------|:--------:|-------------|---------|
39+
| `azure.subscription_id` | string | **Yes** | Azure subscription ID | — |
40+
| `azure.resource_group` | string | **Yes** | Resource group containing the target VMs | — |
41+
| `azure.location` | string | **Yes** | Azure region | `eastus` |
42+
43+
---
44+
45+
## Azure Local
46+
47+
```yaml
48+
azure_local:
49+
custom_location_id: "/subscriptions/.../customLocations/cl-azurelocal-01"
50+
logical_network_id: "/subscriptions/.../logicalNetworks/lnet-mgmt-01"
51+
```
52+
53+
| Variable | Type | Required | Description | Default |
54+
|----------|------|:--------:|-------------|---------|
55+
| `azure_local.custom_location_id` | string | **Yes** | Full ARM resource ID of the Azure Local custom location | — |
56+
| `azure_local.logical_network_id` | string | **Yes** | Full ARM resource ID of the logical network for the converted VM | — |
57+
58+
---
59+
60+
## Conversion Settings
61+
62+
```yaml
63+
conversion:
64+
working_directory: "C:\\ClusterStorage\\Volume01\\Gen2Conversion"
65+
max_parallel: 1
66+
```
67+
68+
| Variable | Type | Required | Description | Default |
69+
|----------|------|:--------:|-------------|---------|
70+
| `conversion.working_directory` | string | **Yes** | Scratch directory for conversion work files (must have sufficient free space) | — |
71+
| `conversion.max_parallel` | integer | No | Number of VMs to process in parallel (`1` = sequential) | `1` |
72+
73+
---
74+
75+
## Tags
76+
77+
```yaml
78+
tags:
79+
project: "VM-Conversion"
80+
environment: "production"
81+
workload: "gen1-to-gen2"
82+
solution: "vmconvert-azure-local"
83+
```
84+
85+
| Variable | Type | Required | Description | Default |
86+
|----------|------|:--------:|-------------|---------|
87+
| `tags.project` | string | No | Project tag | `VM-Conversion` |
88+
| `tags.environment` | string | No | Environment tag | `production` |
89+
| `tags.workload` | string | No | Workload type tag | `gen1-to-gen2` |
90+
| `tags.solution` | string | No | Solution identifier tag | `vmconvert-azure-local` |
91+
92+
---
93+
94+
## Script Parameter Mapping
95+
96+
The table below maps `variables.yml` keys to the actual script parameters:
97+
98+
| Variable Key | Azure Local Script Param | Hyper-V Script Param |
99+
|-------------|-------------------------|---------------------|
100+
| `azure.subscription_id` | `-SubscriptionId` | N/A |
101+
| `azure.resource_group` | `-ResourceGroup` | N/A |
102+
| `azure_local.custom_location_id` | `-CustomLocationId` | N/A |
103+
| `azure_local.logical_network_id` | `-LogicalNetworkId` | N/A |
104+
| `conversion.working_directory` | `-WorkingDirectory` | `-WorkingDirectory` |
105+
| `conversion.max_parallel` | `-MaxParallel` | `-MaxParallel` |

mkdocs.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ nav:
4040
- Azure Local Path: runbook-azurelocal.md
4141
- Hyper-V Path: runbook-hyperv.md
4242
- Troubleshooting: troubleshooting.md
43+
- Reference:
44+
- Variable Reference: reference/variables.md
4345
- Contributing: contributing.md
4446

4547
markdown_extensions:

0 commit comments

Comments
 (0)