Terraform Deployment¶
Overview¶
Deploys all Azure-side resources for the SOFS guest cluster using Terraform with the azapi and azurerm providers, plus Azure Verified Modules (AVM) for the resource group and cloud witness storage account. Azure Local resource types (Microsoft.AzureStackHCI/*) are not fully supported by azurerm alone, so the azapi provider handles Arc VM, NIC, data disk, and domain join extension creation.
After terraform apply, a fully-populated Ansible inventory is auto-generated — feeding directly into the guest configuration phase.
Capability¶
| Capability | Supported |
|---|---|
| Azure resource provisioning | ✅ |
| Domain join (JsonADDomainExtension) | ✅ via azapi |
| Per-VM storage path mapping | ✅ |
| Guest OS configuration | Delegates to PS/Ansible |
| AVM modules (RG + Storage) | ✅ |
Resources Created¶
| Resource | Provider | Type |
|---|---|---|
| Resource Group | azurerm (AVM) |
avm-res-resources-resourcegroup |
| Cloud Witness Storage Account | azurerm (AVM) |
avm-res-storage-storageaccount |
| Arc Machine Placeholders | azapi |
Microsoft.HybridCompute/machines |
| NICs (compute logical network) | azapi |
Microsoft.AzureStackHCI/networkInterfaces |
| Data Disks (S2D pool) | azapi |
Microsoft.AzureStackHCI/virtualHardDisks |
| VM Instances | azapi |
Microsoft.AzureStackHCI/virtualMachineInstances |
| Domain Join Extension | azapi |
Microsoft.HybridCompute/machines/extensions |
| Ansible Inventory File | local |
local_sensitive_file |
Prerequisites¶
- Terraform >= 1.5
- Azure CLI authenticated (
az login) azapiandazurermproviders (auto-installed byterraform init)- All general prerequisites met
File Inventory¶
| File | Purpose |
|---|---|
main.tf |
Provider configuration (azapi, azurerm, local) + AVM module calls |
variables.tf |
All input variables (Azure, VM, domain join, SOFS, single/triple layout, Cloud Cache) |
locals.tf |
Computed values — VM names, disk flattening, pool calculations, single/triple layout |
sofs.tf |
Arc machines, NICs, data disks, VM instances, domain join extensions |
ansible-inventory.tf |
Generates Ansible inventory from Terraform outputs |
ansible-controller.tf |
Optional Ansible controller VM (for ansible_create engine) |
templates/inventory.yml.tftpl |
Ansible inventory template with all SOFS variables |
outputs.tf |
VM IDs, witness key, pool sizing, inventory path |
terraform.tfvars.example |
Example variable values for all 10 scenarios |
tests/ |
Terraform native tests (.tftest.hcl) |
Setup¶
1. Configure Variables¶
Edit terraform.tfvars with values from your config/variables.yml. Key mappings:
| variables.yml | terraform.tfvars |
|---|---|
azure.subscription_id |
subscription_id |
azure.resource_group |
resource_group_name |
azure.location |
location |
azure_local.custom_location_id |
custom_location_id |
azure_local.logical_network_id |
logical_network_id |
azure_local.gallery_image_name |
gallery_image_id |
azure_local.storage_path_ids |
storage_path_ids |
vm.prefix |
vm_prefix |
vm.count |
vm_count |
vm.processors |
vm_processors |
vm.memory_mb |
vm_memory_mb |
vm.ips |
vm_ips |
data_disks.count |
data_disk_count |
data_disks.size_gb |
data_disk_size_gb |
domain.fqdn |
domain_fqdn |
domain.join_username |
domain_join_username |
domain.join_password |
domain_join_password |
deployment.guest_layout |
guest_volume_layout |
deployment.guest_resiliency |
guest_resiliency |
sofs.* |
sofs_* variables |
fslogix.cloud_cache.providers |
cloud_cache_providers |
2. Set Secrets Securely¶
Never put passwords in terraform.tfvars. Use environment variables:
$env:TF_VAR_admin_password = (az keyvault secret show `
--vault-name "kv-platform-prod" `
--name "sofs-vm-admin-password" `
--query value -o tsv)
Deployment¶
Recommended: End-to-End Script¶
The Deploy-SOFS.ps1 orchestration script runs Terraform (Phase 1) then Ansible (Phase 2) in sequence:
Manual: Step-by-Step¶
cd src/terraform
# Initialize providers
terraform init
# Preview changes
terraform plan -var-file="terraform.tfvars"
# Deploy
terraform apply -var-file="terraform.tfvars"
Verify¶
After apply, check the generated Ansible inventory:
# Check for placeholder IPs (need manual update if DHCP)
terraform output ansible_inventory_has_placeholders
If true, VMs were deployed with DHCP and you need to update the inventory with actual IPs before running guest configuration.
VM IP Addresses¶
Terraform does not know VM IPs at provision time when Azure Local assigns them via DHCP. Two options:
Option 1 — Pre-allocate IPs in tfvars (recommended):
Option 2 — Post-provision update:
After VMs are created, discover IPs and update the generated Ansible inventory manually.
Auto-Generated Ansible Inventory¶
Terraform writes an Ansible inventory file at ../ansible/inventory-generated.yml that includes:
- SOFS VM hostnames and IPs
- Cloud witness storage account key (from Terraform output)
- All cluster configuration variables
This eliminates manual inventory creation when using the Terraform → Ansible deployment path.
Post-Deployment¶
After Terraform completes:
- Verify VMs are running in Azure portal or via
az stack-hci-vm list - Verify domain join — the
JsonADDomainExtensionis deployed automatically - Run guest configuration using PowerShell or Ansible
Guest Configuration Engine¶
Terraform delegates guest OS configuration to either PowerShell or Ansible via the guest_config_engine variable:
| Value | Behavior |
|---|---|
powershell (default) |
After terraform apply, run Configure-SOFS-Cluster.ps1 manually |
ansible_create |
Deploy a Linux Ansible controller VM, then run playbooks automatically |
ansible_existing |
Use an existing Ansible controller to run playbooks |
When using the Ansible path, Terraform auto-generates ../ansible/inventory-generated.yml with all values populated from Terraform outputs.
Testing¶
Terraform native tests validate variable constraints and local value computations without requiring cloud access:
Test files in tests/:
variables.tftest.hcl— validates vm_count range (2–16), required variables, single/triple layout defaultslocals.tftest.hcl— validates VM name generation, disk flattening, pool calculations
Destroy¶
To tear down all Azure resources:
Warning
This destroys the resource group and all resources. Data on the SOFS shares will be lost. The Azure Local host volumes are not managed by Terraform and are unaffected.
Next Steps¶
- Ansible — Guest OS configuration (Phases 3–11) using the auto-generated inventory
- PowerShell — Alternative guest configuration via PowerShell remoting
- Validation — Verify the deployment