Handoff
This commit is contained in:
509
HANDOFF.md
Normal file
509
HANDOFF.md
Normal file
@@ -0,0 +1,509 @@
|
|||||||
|
# Hyper-V Automation Project - Handoff Document
|
||||||
|
|
||||||
|
**Date**: 2026-04-29
|
||||||
|
**Status**: Development - Core provisioning implemented
|
||||||
|
**Next Session**: Ready for Hyper-V host setup and VM testing
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
Enterprise-grade automation for Windows Server VM lifecycle management on Hyper-V using Ansible Automation Platform. Demonstrates GitOps and Infrastructure as Code principles for demo/lab environments.
|
||||||
|
|
||||||
|
**Primary Use Case**: Automated VM provisioning with unattended Windows installation
|
||||||
|
|
||||||
|
**Target Environment**: Demo/Lab (not production-hardened)
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
|
||||||
|
### ✅ Completed
|
||||||
|
|
||||||
|
1. **Project Structure**
|
||||||
|
- Integrated with toallab standard inventory (`/home/ptoal/Dev/inventories/toallab-inventory`)
|
||||||
|
- Group variables organized (hyperv, windows_servers, web_servers)
|
||||||
|
- Host variables configured for hyperv1.lan.toal.ca (192.168.1.182)
|
||||||
|
- Collections requirements defined
|
||||||
|
|
||||||
|
2. **Hyper-V Host Provisioning** ⭐ NEW
|
||||||
|
- Playbook: `playbooks/provision-hyperv-host.yml`
|
||||||
|
- Installs Hyper-V role and management tools
|
||||||
|
- Creates storage structure (D:\VMs, D:\ISOs, D:\Templates)
|
||||||
|
- Configures virtual switches (External-NAT, Internal-Lab)
|
||||||
|
- Sets up NAT networking (192.168.100.0/24)
|
||||||
|
- Hardens WinRM with HTTPS listener
|
||||||
|
- Demo-optimized settings (power, firewall, etc.)
|
||||||
|
|
||||||
|
3. **VM Provisioning Workflow**
|
||||||
|
- Playbook: `playbooks/provision-vm.yml`
|
||||||
|
- Creates VMs with configurable resources (CPU, RAM, disk)
|
||||||
|
- Generates autounattend.xml for unattended Windows installation
|
||||||
|
- Validates virtual switch availability
|
||||||
|
- Supports tags for selective execution (create/install/verify)
|
||||||
|
- Idempotent operations
|
||||||
|
- AAP-ready with survey support
|
||||||
|
|
||||||
|
4. **Templates**
|
||||||
|
- `templates/autounattend.xml.j2` - Complete Windows unattended install
|
||||||
|
- Configures: network, WinRM, RDP, computer name, timezone, admin password
|
||||||
|
- Static IP or DHCP support
|
||||||
|
- First-logon commands for Ansible readiness
|
||||||
|
|
||||||
|
5. **Helper Playbooks**
|
||||||
|
- `playbooks/list-hyperv-switches.yml` - Discover available switches
|
||||||
|
- `playbooks/create-autounattend-iso.yml` - Helper for ISO creation
|
||||||
|
- `playbooks/install-iis.yml` - IIS deployment demo (existing)
|
||||||
|
- `playbooks/patch-vms.yml` - Windows Update automation (existing)
|
||||||
|
|
||||||
|
6. **Documentation**
|
||||||
|
- `QUICKSTART.md` - Complete step-by-step guide from scratch
|
||||||
|
- `playbooks/README-provision.md` - Detailed VM provisioning guide
|
||||||
|
- `CLAUDE.md` - Architecture and development guidelines
|
||||||
|
- `README.md` - Project overview
|
||||||
|
|
||||||
|
### ⚠️ Known Issues / Limitations
|
||||||
|
|
||||||
|
1. **Virtual Switch Discovery**
|
||||||
|
- Fixed: Playbook now validates switches before VM creation
|
||||||
|
- Default switch changed from "Internal Switch" → "External-NAT"
|
||||||
|
- Run `list-hyperv-switches.yml` to see available switches
|
||||||
|
|
||||||
|
2. **AutoUnattend.xml Delivery**
|
||||||
|
- Currently requires manual intervention or helper playbook
|
||||||
|
- Windows Setup needs to find autounattend.xml (second DVD drive, floppy, or custom ISO)
|
||||||
|
- Helper playbook provided but requires Windows ADK on host
|
||||||
|
|
||||||
|
3. **Circular Variable References**
|
||||||
|
- Fixed: Moved variable defaults from `vars:` to `set_fact` in `pre_tasks`
|
||||||
|
- Variables now compute correctly with extra vars or group_vars fallbacks
|
||||||
|
|
||||||
|
4. **WinRM Configuration**
|
||||||
|
- Currently using NTLM over HTTP (port 5985) for hyperv1.lan.toal.ca
|
||||||
|
- Demo-appropriate but not production-ready
|
||||||
|
- Host provisioning playbook sets up HTTPS for new hosts
|
||||||
|
|
||||||
|
### 🔄 In Progress
|
||||||
|
|
||||||
|
- **Testing**: Hyper-V host provisioning playbook not yet tested on actual host
|
||||||
|
- **VM Provisioning**: Ready to test once host is configured
|
||||||
|
- **AutoUnattend ISO**: Helper playbook exists but untested
|
||||||
|
|
||||||
|
### ❌ Not Yet Implemented
|
||||||
|
|
||||||
|
1. **Windows Baseline Configuration** (future role)
|
||||||
|
- Security hardening
|
||||||
|
- Monitoring agent installation
|
||||||
|
- Compliance scanning
|
||||||
|
|
||||||
|
2. **ServiceNow CMDB Integration**
|
||||||
|
- Playbook exists (`sync-cmdb.yml`) but needs implementation
|
||||||
|
- Requires ServiceNow credentials and table configuration
|
||||||
|
|
||||||
|
3. **Custom Execution Environment**
|
||||||
|
- Currently using `aap.toal.ca/ee-demo`
|
||||||
|
- Could build project-specific EE with all dependencies
|
||||||
|
|
||||||
|
4. **Event-Driven Ansible**
|
||||||
|
- Phase 3 enhancement
|
||||||
|
- React to Hyper-V events, ServiceNow incidents, etc.
|
||||||
|
|
||||||
|
5. **VM Templates**
|
||||||
|
- Sysprep and convert base VM to reusable template
|
||||||
|
- Faster provisioning than full install
|
||||||
|
|
||||||
|
## Inventory Configuration
|
||||||
|
|
||||||
|
### Hypervisor
|
||||||
|
|
||||||
|
**Host**: `hyperv1.lan.toal.ca`
|
||||||
|
**IP**: `192.168.1.182`
|
||||||
|
**Connection**: WinRM over HTTP (NTLM auth, port 5985)
|
||||||
|
**Group**: `hyperv`
|
||||||
|
|
||||||
|
**Inventory Location**: `/home/ptoal/Dev/inventories/toallab-inventory/static.yml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
hyperv:
|
||||||
|
hosts:
|
||||||
|
hyperv1.lan.toal.ca:
|
||||||
|
ansible_host: 192.168.1.182
|
||||||
|
```
|
||||||
|
|
||||||
|
### Group Variables
|
||||||
|
|
||||||
|
**Location**: `/home/ptoal/Dev/inventories/toallab-inventory/group_vars/`
|
||||||
|
|
||||||
|
**hyperv/vars.yml**:
|
||||||
|
```yaml
|
||||||
|
default_vm_cpu_count: 2
|
||||||
|
default_vm_memory_gb: 4
|
||||||
|
default_vm_disk_size_gb: 60
|
||||||
|
default_vm_switch: "External-NAT" # Changed from "Internal Switch"
|
||||||
|
vm_storage_path: "D:\\VMs"
|
||||||
|
iso_storage_path: "D:\\ISOs"
|
||||||
|
windows_server_iso: "D:\\ISOs\\Windows_Server_2022.iso"
|
||||||
|
```
|
||||||
|
|
||||||
|
**windows_servers/vars.yml**:
|
||||||
|
- Windows Update categories
|
||||||
|
- DNS servers
|
||||||
|
- Timezone settings
|
||||||
|
- Features to remove
|
||||||
|
|
||||||
|
**web_servers/vars.yml**:
|
||||||
|
- IIS features list
|
||||||
|
- Application pool settings
|
||||||
|
- Website configuration
|
||||||
|
|
||||||
|
### Host Variables
|
||||||
|
|
||||||
|
**hyperv1.lan.toal.ca/vars.yml**:
|
||||||
|
```yaml
|
||||||
|
ansible_connection: winrm
|
||||||
|
ansible_winrm_transport: ntlm
|
||||||
|
ansible_winrm_server_cert_validation: ignore
|
||||||
|
ansible_port: 5985
|
||||||
|
```
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
### Immediate (Next Session)
|
||||||
|
|
||||||
|
1. **Configure Hyper-V Host** (if fresh install)
|
||||||
|
```bash
|
||||||
|
source ~/.venv/ansible/bin/activate
|
||||||
|
cd /home/ptoal/Dev/Projects/HyperV
|
||||||
|
|
||||||
|
# Test connectivity
|
||||||
|
ansible hyperv -m ansible.windows.win_ping
|
||||||
|
|
||||||
|
# Configure host
|
||||||
|
ansible-playbook playbooks/provision-hyperv-host.yml
|
||||||
|
|
||||||
|
# Verify switches
|
||||||
|
ansible-playbook playbooks/list-hyperv-switches.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Upload Windows Server ISO**
|
||||||
|
- Copy ISO to D:\ISOs\Windows_Server_2022.iso on Hyper-V host
|
||||||
|
- Or download directly on host
|
||||||
|
- Verify path matches `windows_server_iso` in group_vars
|
||||||
|
|
||||||
|
3. **Test VM Provisioning**
|
||||||
|
```bash
|
||||||
|
# Provision first test VM
|
||||||
|
ansible-playbook playbooks/provision-vm.yml \
|
||||||
|
-e vm_name=TEST01 \
|
||||||
|
-e vm_ip_address=192.168.100.10
|
||||||
|
|
||||||
|
# Monitor installation via Hyper-V console
|
||||||
|
# Wait for WinRM to become available (~20-30 min)
|
||||||
|
|
||||||
|
# Test connectivity
|
||||||
|
ansible TEST01 -i "192.168.100.10," -m ansible.windows.win_ping
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Add VM to Inventory**
|
||||||
|
```bash
|
||||||
|
vi /home/ptoal/Dev/inventories/toallab-inventory/static.yml
|
||||||
|
# Add under web_servers or appropriate group
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Test Application Deployment**
|
||||||
|
```bash
|
||||||
|
# Deploy IIS
|
||||||
|
ansible-playbook playbooks/install-iis.yml --limit TEST01
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
curl http://192.168.100.10
|
||||||
|
```
|
||||||
|
|
||||||
|
### Short Term (Next Few Sessions)
|
||||||
|
|
||||||
|
1. **Resolve AutoUnattend Delivery**
|
||||||
|
- Test `create-autounattend-iso.yml` helper
|
||||||
|
- OR: Create custom Windows ISO with embedded autounattend.xml
|
||||||
|
- OR: Document manual second-DVD-drive approach
|
||||||
|
- Update provisioning playbook with working method
|
||||||
|
|
||||||
|
2. **Create Windows Baseline Role**
|
||||||
|
```
|
||||||
|
roles/windows_baseline/
|
||||||
|
├── tasks/
|
||||||
|
│ ├── main.yml
|
||||||
|
│ ├── security.yml
|
||||||
|
│ ├── monitoring.yml
|
||||||
|
│ └── compliance.yml
|
||||||
|
├── templates/
|
||||||
|
└── defaults/
|
||||||
|
```
|
||||||
|
- Security hardening (CIS benchmarks subset)
|
||||||
|
- Time sync with domain/NTP
|
||||||
|
- Windows Update configuration
|
||||||
|
- Logging and auditing
|
||||||
|
- Monitoring agent (if available)
|
||||||
|
|
||||||
|
3. **Implement ServiceNow CMDB Sync**
|
||||||
|
- Test ServiceNow API connectivity
|
||||||
|
- Implement `sync-cmdb.yml` playbook
|
||||||
|
- Create/update CI records
|
||||||
|
- Sync on VM create/update/delete
|
||||||
|
|
||||||
|
4. **Test Workflow in AAP**
|
||||||
|
- Create job templates with surveys
|
||||||
|
- Test webhook integration
|
||||||
|
- Create workflow template (provision → baseline → deploy → CMDB)
|
||||||
|
|
||||||
|
### Medium Term
|
||||||
|
|
||||||
|
1. **VM Template Creation**
|
||||||
|
- Build golden image VM
|
||||||
|
- Sysprep and generalize
|
||||||
|
- Convert to template/library
|
||||||
|
- Create playbook to clone from template (faster than full install)
|
||||||
|
|
||||||
|
2. **Backup and Recovery**
|
||||||
|
- Hyper-V checkpoint management playbook
|
||||||
|
- Export/import VM playbooks
|
||||||
|
- Backup scheduling
|
||||||
|
|
||||||
|
3. **Network Configuration**
|
||||||
|
- VLAN tagging playbooks
|
||||||
|
- Multiple NIC configuration
|
||||||
|
- DNS/DHCP integration
|
||||||
|
|
||||||
|
4. **Application Deployment**
|
||||||
|
- Expand beyond IIS demo
|
||||||
|
- SQL Server installation role
|
||||||
|
- Custom application deployment patterns
|
||||||
|
|
||||||
|
### Long Term (Future Phases)
|
||||||
|
|
||||||
|
1. **Event-Driven Ansible**
|
||||||
|
- ServiceNow incident → remediation playbook
|
||||||
|
- Hyper-V event monitoring → capacity management
|
||||||
|
- Windows Event Log → security response
|
||||||
|
|
||||||
|
2. **Multi-Host Hyper-V**
|
||||||
|
- Cluster configuration
|
||||||
|
- Live migration support
|
||||||
|
- Shared storage
|
||||||
|
|
||||||
|
3. **Advanced Features**
|
||||||
|
- Nested virtualization
|
||||||
|
- Container deployment on Windows VMs
|
||||||
|
- Azure Arc integration
|
||||||
|
|
||||||
|
## Key Files Reference
|
||||||
|
|
||||||
|
### Playbooks
|
||||||
|
```
|
||||||
|
playbooks/
|
||||||
|
├── provision-hyperv-host.yml # Configure Hyper-V host (one-time)
|
||||||
|
├── provision-vm.yml # Create Windows VMs
|
||||||
|
├── list-hyperv-switches.yml # Discover switches
|
||||||
|
├── create-autounattend-iso.yml # AutoUnattend helper
|
||||||
|
├── install-iis.yml # IIS deployment
|
||||||
|
├── patch-vms.yml # Windows Updates
|
||||||
|
└── sync-cmdb.yml # ServiceNow (stub)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
```
|
||||||
|
ansible.cfg # Points to toallab-inventory
|
||||||
|
collections/requirements.yml # Required collections
|
||||||
|
templates/autounattend.xml.j2 # Windows unattended install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
```
|
||||||
|
README.md # Project overview
|
||||||
|
QUICKSTART.md # Step-by-step guide
|
||||||
|
HANDOFF.md # This file
|
||||||
|
CLAUDE.md # Architecture details
|
||||||
|
playbooks/README-provision.md # Provisioning deep-dive
|
||||||
|
```
|
||||||
|
|
||||||
|
### Inventory (External)
|
||||||
|
```
|
||||||
|
/home/ptoal/Dev/inventories/toallab-inventory/
|
||||||
|
├── static.yml # Hypervisor and VMs
|
||||||
|
├── group_vars/
|
||||||
|
│ ├── hyperv/vars.yml
|
||||||
|
│ ├── windows_servers/vars.yml
|
||||||
|
│ └── web_servers/vars.yml
|
||||||
|
└── host_vars/
|
||||||
|
└── hyperv1.lan.toal.ca/vars.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Reference Commands
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
```bash
|
||||||
|
# Activate environment
|
||||||
|
source ~/.venv/ansible/bin/activate
|
||||||
|
cd /home/ptoal/Dev/Projects/HyperV
|
||||||
|
|
||||||
|
# Test connectivity
|
||||||
|
ansible hyperv -m ansible.windows.win_ping
|
||||||
|
|
||||||
|
# Configure host (one-time)
|
||||||
|
ansible-playbook playbooks/provision-hyperv-host.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### VM Operations
|
||||||
|
```bash
|
||||||
|
# List switches
|
||||||
|
ansible-playbook playbooks/list-hyperv-switches.yml
|
||||||
|
|
||||||
|
# Provision VM
|
||||||
|
ansible-playbook playbooks/provision-vm.yml \
|
||||||
|
-e vm_name=WEB01 \
|
||||||
|
-e vm_ip_address=192.168.100.10
|
||||||
|
|
||||||
|
# List all VMs
|
||||||
|
ansible hyperv -m ansible.windows.win_shell \
|
||||||
|
-a "Get-VM | Select-Object Name, State, CPUUsage | Format-Table"
|
||||||
|
|
||||||
|
# VM power operations
|
||||||
|
ansible hyperv -m ansible.windows.win_shell -a "Start-VM -Name WEB01"
|
||||||
|
ansible hyperv -m ansible.windows.win_shell -a "Stop-VM -Name WEB01 -Force"
|
||||||
|
ansible hyperv -m ansible.windows.win_shell -a "Remove-VM -Name WEB01 -Force"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Application Deployment
|
||||||
|
```bash
|
||||||
|
# Deploy IIS
|
||||||
|
ansible-playbook playbooks/install-iis.yml --limit WEB01
|
||||||
|
|
||||||
|
# Patch VMs
|
||||||
|
ansible-playbook playbooks/patch-vms.yml --limit windows_servers
|
||||||
|
|
||||||
|
# Test connectivity to VM
|
||||||
|
ansible WEB01 -m ansible.windows.win_ping
|
||||||
|
```
|
||||||
|
|
||||||
|
### Troubleshooting
|
||||||
|
```bash
|
||||||
|
# Verbose output
|
||||||
|
ansible hyperv -m ansible.windows.win_ping -vvv
|
||||||
|
|
||||||
|
# Check WinRM
|
||||||
|
ansible hyperv -m ansible.windows.win_shell -a "Get-Service WinRM"
|
||||||
|
|
||||||
|
# Verify paths
|
||||||
|
ansible hyperv -m ansible.windows.win_stat -a "path=D:\\ISOs\\Windows_Server_2022.iso"
|
||||||
|
|
||||||
|
# Get Hyper-V info
|
||||||
|
ansible hyperv -m ansible.windows.win_shell -a "Get-VMHost | ConvertTo-Json"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
### Security Considerations
|
||||||
|
⚠️ **Current configuration is for DEMO/LAB only**
|
||||||
|
|
||||||
|
**Current State**:
|
||||||
|
- WinRM over HTTP (not HTTPS)
|
||||||
|
- NTLM authentication (not Kerberos)
|
||||||
|
- Certificate validation disabled
|
||||||
|
- Permissive firewall rules
|
||||||
|
- No backup/DR
|
||||||
|
- No monitoring
|
||||||
|
|
||||||
|
**For Production**:
|
||||||
|
- Enable HTTPS for WinRM (port 5986)
|
||||||
|
- Use Kerberos authentication
|
||||||
|
- Enable certificate validation
|
||||||
|
- Implement network segmentation
|
||||||
|
- Configure backup and DR
|
||||||
|
- Deploy monitoring/alerting
|
||||||
|
- Implement change management
|
||||||
|
- Regular security patching
|
||||||
|
- Compliance scanning
|
||||||
|
|
||||||
|
### AutoUnattend.xml Gotchas
|
||||||
|
|
||||||
|
1. **Delivery Methods** (in order of difficulty):
|
||||||
|
- Second DVD drive (easiest, use helper playbook)
|
||||||
|
- Rebuild ISO with autounattend in root (moderate)
|
||||||
|
- Floppy image (legacy, difficult)
|
||||||
|
|
||||||
|
2. **Common Issues**:
|
||||||
|
- File not found → wrong location
|
||||||
|
- Interactive prompts → XML syntax error
|
||||||
|
- Network not configured → check IP settings in template
|
||||||
|
- WinRM not available → first logon commands didn't run
|
||||||
|
|
||||||
|
3. **Debugging**:
|
||||||
|
- Check logs: `C:\Windows\Panther\setupact.log`
|
||||||
|
- Watch VM console during install
|
||||||
|
- Verify XML with validator before use
|
||||||
|
|
||||||
|
### Variable Precedence Reminder
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Extra vars (-e on command line) [highest]
|
||||||
|
2. Task vars (in playbook)
|
||||||
|
3. Host vars (host_vars/)
|
||||||
|
4. Group vars (group_vars/)
|
||||||
|
5. Role defaults
|
||||||
|
6. Inventory vars [lowest]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Checklist
|
||||||
|
|
||||||
|
Before declaring completion:
|
||||||
|
|
||||||
|
- [ ] Hyper-V host provisioning successful
|
||||||
|
- [ ] Virtual switches created and accessible
|
||||||
|
- [ ] VM provisioning creates VM successfully
|
||||||
|
- [ ] AutoUnattend.xml performs unattended install
|
||||||
|
- [ ] WinRM available after installation
|
||||||
|
- [ ] VM pingable from Ansible
|
||||||
|
- [ ] IIS deployment works
|
||||||
|
- [ ] Windows Update playbook works
|
||||||
|
- [ ] AAP job template with survey works
|
||||||
|
- [ ] Documentation is accurate
|
||||||
|
|
||||||
|
## Questions for Next Session
|
||||||
|
|
||||||
|
1. **Hyper-V Host Status**:
|
||||||
|
- Is hyperv1.lan.toal.ca a fresh install or already configured?
|
||||||
|
- Are virtual switches already created?
|
||||||
|
- Is storage already set up?
|
||||||
|
|
||||||
|
2. **ISO Availability**:
|
||||||
|
- Is Windows Server 2022 ISO available?
|
||||||
|
- Where is it located?
|
||||||
|
|
||||||
|
3. **Network**:
|
||||||
|
- What network should VMs be on?
|
||||||
|
- Static IPs or DHCP?
|
||||||
|
- Internet access required?
|
||||||
|
|
||||||
|
4. **Scope**:
|
||||||
|
- How many VMs to provision initially?
|
||||||
|
- What applications to deploy?
|
||||||
|
- Integration with existing systems needed?
|
||||||
|
|
||||||
|
## Contact / References
|
||||||
|
|
||||||
|
**Project Location**: `/home/ptoal/Dev/Projects/HyperV`
|
||||||
|
**Inventory Location**: `/home/ptoal/Dev/inventories/toallab-inventory`
|
||||||
|
**Virtual Environment**: `~/.venv/ansible`
|
||||||
|
**Execution Environment**: `aap.toal.ca/ee-demo`
|
||||||
|
|
||||||
|
**Key Technologies**:
|
||||||
|
- Ansible Core 2.15+
|
||||||
|
- Ansible Automation Platform 2.x
|
||||||
|
- Microsoft Hyper-V (Windows Server 2019/2022)
|
||||||
|
- Windows Server 2019/2022
|
||||||
|
|
||||||
|
**Collections Used**:
|
||||||
|
- ansible.windows (>=2.0.0)
|
||||||
|
- community.windows (>=2.0.0)
|
||||||
|
- servicenow.itsm (>=2.0.0)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Ready for next session**: Yes
|
||||||
|
**Blockers**: None - ready to test on actual Hyper-V host
|
||||||
|
**Recommended first task**: Run `provision-hyperv-host.yml` to set up the environment
|
||||||
394
QUICKSTART.md
Normal file
394
QUICKSTART.md
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
# Hyper-V Automation - Quick Start Guide
|
||||||
|
|
||||||
|
This guide walks through setting up a Hyper-V host and provisioning Windows VMs using Ansible.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Fresh Windows Server 2019/2022 installation
|
||||||
|
- Network connectivity from Ansible control node
|
||||||
|
- Windows Server ISO image
|
||||||
|
|
||||||
|
## Step 1: Initial Hyper-V Host Setup
|
||||||
|
|
||||||
|
### 1.1 Configure WinRM on Hyper-V Host
|
||||||
|
|
||||||
|
On the Windows Server (as Administrator):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Enable PowerShell remoting
|
||||||
|
Enable-PSRemoting -Force
|
||||||
|
|
||||||
|
# Configure WinRM for Ansible
|
||||||
|
winrm quickconfig -transport:http
|
||||||
|
Set-Item WSMan:\localhost\Service\Auth\Basic -Value $true
|
||||||
|
Set-Item WSMan:\localhost\Service\Auth\NTLM -Value $true
|
||||||
|
|
||||||
|
# Allow unencrypted traffic for initial setup (demo only!)
|
||||||
|
Set-Item WSMan:\localhost\Service\AllowUnencrypted -Value $true
|
||||||
|
|
||||||
|
# Configure firewall
|
||||||
|
Enable-NetFirewallRule -Name "WINRM-HTTP-In-TCP"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 Test Ansible Connectivity
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Activate virtual environment
|
||||||
|
source ~/.venv/ansible/bin/activate
|
||||||
|
|
||||||
|
# Test connection
|
||||||
|
ansible hyperv -m ansible.windows.win_ping
|
||||||
|
|
||||||
|
# If successful, you should see:
|
||||||
|
# hyperv1.lan.toal.ca | SUCCESS => {
|
||||||
|
# "changed": false,
|
||||||
|
# "ping": "pong"
|
||||||
|
# }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2: Provision Hyper-V Host
|
||||||
|
|
||||||
|
This playbook configures the Hyper-V host with:
|
||||||
|
- Hyper-V role and management tools
|
||||||
|
- Storage directories for VMs and ISOs
|
||||||
|
- Virtual switches (External-NAT and Internal-Lab)
|
||||||
|
- NAT networking for VM internet access
|
||||||
|
- WinRM security hardening
|
||||||
|
- Demo-appropriate settings
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run the provisioning playbook
|
||||||
|
ansible-playbook playbooks/provision-hyperv-host.yml
|
||||||
|
|
||||||
|
# This takes 5-10 minutes and may reboot the host
|
||||||
|
```
|
||||||
|
|
||||||
|
### What Gets Created
|
||||||
|
|
||||||
|
```
|
||||||
|
D:\
|
||||||
|
├── VMs\ # VM storage
|
||||||
|
├── ISOs\ # ISO images
|
||||||
|
└── Templates\ # VHD templates (future)
|
||||||
|
|
||||||
|
Virtual Switches:
|
||||||
|
├── External-NAT # For internet access via NAT
|
||||||
|
└── Internal-Lab # Isolated internal network
|
||||||
|
|
||||||
|
NAT Network: 192.168.100.0/24
|
||||||
|
Gateway: 192.168.100.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 3: Upload Windows ISO
|
||||||
|
|
||||||
|
### Option 1: Manual Upload
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# On Hyper-V host or via RDP
|
||||||
|
# Copy ISO to D:\ISOs\Windows_Server_2022.iso
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Ansible Copy (slower)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From Ansible control node
|
||||||
|
ansible hyperv -m ansible.windows.win_copy \
|
||||||
|
-a "src=/path/to/Windows_Server_2022.iso dest=D:\\ISOs\\Windows_Server_2022.iso"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 3: Direct Download (if internet available)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible hyperv -m ansible.windows.win_get_url \
|
||||||
|
-a "url=https://your-iso-source/Windows_Server_2022.iso dest=D:\\ISOs\\Windows_Server_2022.iso"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 4: Verify Configuration
|
||||||
|
|
||||||
|
### 4.1 List Available Virtual Switches
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible-playbook playbooks/list-hyperv-switches.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output:
|
||||||
|
```
|
||||||
|
Available Virtual Switches:
|
||||||
|
- External-NAT
|
||||||
|
- Internal-Lab
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 Verify ISO Path
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible hyperv -m ansible.windows.win_stat \
|
||||||
|
-a "path=D:\\ISOs\\Windows_Server_2022.iso"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 5: Provision Your First VM
|
||||||
|
|
||||||
|
### 5.1 Basic VM Provisioning
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible-playbook playbooks/provision-vm.yml \
|
||||||
|
-e vm_name=WEB01 \
|
||||||
|
-e vm_ip_address=192.168.100.10
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 Custom VM Configuration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible-playbook playbooks/provision-vm.yml \
|
||||||
|
-e vm_name=APP01 \
|
||||||
|
-e vm_ip_address=192.168.100.20 \
|
||||||
|
-e vm_cpu_count=4 \
|
||||||
|
-e vm_memory_gb=8 \
|
||||||
|
-e vm_disk_size_gb=100
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 What Happens
|
||||||
|
|
||||||
|
1. ✓ Creates VM with specified resources
|
||||||
|
2. ✓ Generates autounattend.xml for unattended installation
|
||||||
|
3. ✓ Attaches Windows Server ISO
|
||||||
|
4. ✓ Starts VM
|
||||||
|
5. ⏸ Waits for you to verify installation (15-30 min)
|
||||||
|
6. ✓ Verifies WinRM connectivity
|
||||||
|
|
||||||
|
## Step 6: Monitor Installation
|
||||||
|
|
||||||
|
### Via Hyper-V Manager
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# On Hyper-V host
|
||||||
|
vmconnect.exe localhost WEB01
|
||||||
|
```
|
||||||
|
|
||||||
|
### Via PowerShell
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible hyperv -m ansible.windows.win_shell \
|
||||||
|
-a "Get-VM WEB01 | Select-Object Name, State, CPUUsage, Uptime"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Installation Progress
|
||||||
|
|
||||||
|
- **0-5 min**: Windows Setup boots from ISO
|
||||||
|
- **5-20 min**: Windows installation (with autounattend.xml)
|
||||||
|
- **20-25 min**: First boot and configuration
|
||||||
|
- **25-30 min**: WinRM configuration completes
|
||||||
|
|
||||||
|
## Step 7: Add VM to Inventory
|
||||||
|
|
||||||
|
Once installation completes:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Edit inventory
|
||||||
|
vi /home/ptoal/Dev/inventories/toallab-inventory/static.yml
|
||||||
|
|
||||||
|
# Add under web_servers:
|
||||||
|
web_servers:
|
||||||
|
hosts:
|
||||||
|
WEB01:
|
||||||
|
ansible_host: 192.168.100.10
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 8: Verify VM Connectivity
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test WinRM
|
||||||
|
ansible WEB01 -m ansible.windows.win_ping
|
||||||
|
|
||||||
|
# Gather facts
|
||||||
|
ansible WEB01 -m ansible.windows.setup
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 9: Deploy Applications
|
||||||
|
|
||||||
|
### Install IIS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible-playbook playbooks/install-iis.yml --limit WEB01
|
||||||
|
```
|
||||||
|
|
||||||
|
### Apply Windows Updates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible-playbook playbooks/patch-vms.yml --limit WEB01
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Tasks
|
||||||
|
|
||||||
|
### List All VMs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible hyperv -m ansible.windows.win_shell \
|
||||||
|
-a "Get-VM | Select-Object Name, State, CPUUsage | Format-Table"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Start/Stop VM
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start
|
||||||
|
ansible hyperv -m ansible.windows.win_shell -a "Start-VM -Name WEB01"
|
||||||
|
|
||||||
|
# Stop
|
||||||
|
ansible hyperv -m ansible.windows.win_shell -a "Stop-VM -Name WEB01"
|
||||||
|
|
||||||
|
# Shutdown gracefully
|
||||||
|
ansible hyperv -m ansible.windows.win_shell -a "Stop-VM -Name WEB01 -Force"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Delete VM
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible hyperv -m ansible.windows.win_shell -a "Remove-VM -Name WEB01 -Force"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check VM State
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible hyperv -m ansible.windows.win_shell \
|
||||||
|
-a "Get-VM WEB01 | ConvertTo-Json"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### WinRM Connection Failed
|
||||||
|
|
||||||
|
**Problem**: `unreachable` or `connection timeout`
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
1. Verify firewall allows WinRM:
|
||||||
|
```powershell
|
||||||
|
Get-NetFirewallRule -Name "WINRM-HTTP-In-TCP" | Select-Object Name, Enabled
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Check WinRM service:
|
||||||
|
```powershell
|
||||||
|
Get-Service WinRM
|
||||||
|
winrm enumerate winrm/config/listener
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Test from Ansible host:
|
||||||
|
```bash
|
||||||
|
ansible hyperv -m ansible.windows.win_ping -vvv
|
||||||
|
```
|
||||||
|
|
||||||
|
### Virtual Switch Not Found
|
||||||
|
|
||||||
|
**Problem**: `Hyper-V was unable to find a virtual switch`
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
1. List available switches:
|
||||||
|
```bash
|
||||||
|
ansible-playbook playbooks/list-hyperv-switches.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Update group_vars or pass correct switch:
|
||||||
|
```bash
|
||||||
|
-e vm_switch="External-NAT"
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Create missing switch on Hyper-V host:
|
||||||
|
```powershell
|
||||||
|
New-VMSwitch -Name "External-NAT" -SwitchType External -NetAdapterName "Ethernet"
|
||||||
|
```
|
||||||
|
|
||||||
|
### AutoUnattend Not Working
|
||||||
|
|
||||||
|
**Problem**: Windows installation shows interactive prompts
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
1. Verify autounattend.xml was created:
|
||||||
|
```bash
|
||||||
|
ansible hyperv -m ansible.windows.win_stat \
|
||||||
|
-a "path=D:\\VMs\\WEB01\\autounattend.xml"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Check XML syntax in file
|
||||||
|
|
||||||
|
3. Mount as second DVD drive (requires manual step or helper playbook)
|
||||||
|
|
||||||
|
4. Review Windows Setup logs on VM:
|
||||||
|
```
|
||||||
|
C:\Windows\Panther\setupact.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### VM Won't Start
|
||||||
|
|
||||||
|
**Problem**: `failed to start` or `invalid configuration`
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
1. Check VM configuration:
|
||||||
|
```bash
|
||||||
|
ansible hyperv -m ansible.windows.win_shell \
|
||||||
|
-a "Get-VM WEB01 | Select-Object *"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Verify VHD exists:
|
||||||
|
```bash
|
||||||
|
ansible hyperv -m ansible.windows.win_stat \
|
||||||
|
-a "path=D:\\VMs\\WEB01\\WEB01.vhdx"
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Check Hyper-V event logs:
|
||||||
|
```powershell
|
||||||
|
Get-EventLog -LogName "Microsoft-Windows-Hyper-V-*" -Newest 20
|
||||||
|
```
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
/home/ptoal/Dev/Projects/HyperV/
|
||||||
|
├── playbooks/
|
||||||
|
│ ├── provision-hyperv-host.yml # Configure Hyper-V host
|
||||||
|
│ ├── provision-vm.yml # Create VMs
|
||||||
|
│ ├── list-hyperv-switches.yml # List available switches
|
||||||
|
│ ├── install-iis.yml # Deploy IIS
|
||||||
|
│ └── patch-vms.yml # Windows Updates
|
||||||
|
├── templates/
|
||||||
|
│ └── autounattend.xml.j2 # Unattended install template
|
||||||
|
└── ansible.cfg # Project config
|
||||||
|
|
||||||
|
/home/ptoal/Dev/inventories/toallab-inventory/
|
||||||
|
├── static.yml # Static inventory
|
||||||
|
├── group_vars/
|
||||||
|
│ ├── hyperv/vars.yml # Hyper-V defaults
|
||||||
|
│ └── windows_servers/vars.yml # Windows defaults
|
||||||
|
└── host_vars/
|
||||||
|
└── hyperv1.lan.toal.ca/vars.yml # Host-specific config
|
||||||
|
```
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. **Create VM templates** - Sysprep a base VM and convert to template
|
||||||
|
2. **Implement backups** - Use Hyper-V checkpoints or backup playbooks
|
||||||
|
3. **Configure monitoring** - Integrate with Grafana/Prometheus
|
||||||
|
4. **Domain join** - Add VMs to Active Directory
|
||||||
|
5. **Application deployment** - Deploy real applications beyond IIS demo
|
||||||
|
6. **CMDB sync** - Implement ServiceNow integration
|
||||||
|
7. **Event-Driven Ansible** - React to Hyper-V events automatically
|
||||||
|
|
||||||
|
## Production Considerations
|
||||||
|
|
||||||
|
⚠️ **This is a demo configuration.** For production:
|
||||||
|
|
||||||
|
- [ ] Enable HTTPS for WinRM (not HTTP)
|
||||||
|
- [ ] Use Kerberos authentication (not NTLM/Basic)
|
||||||
|
- [ ] Configure storage on SAN/redundant storage
|
||||||
|
- [ ] Implement Hyper-V clustering for HA
|
||||||
|
- [ ] Network segmentation and VLANs
|
||||||
|
- [ ] Security hardening (CIS benchmarks)
|
||||||
|
- [ ] Backup and disaster recovery
|
||||||
|
- [ ] Monitoring and alerting
|
||||||
|
- [ ] Change management and approvals
|
||||||
|
- [ ] Documentation and runbooks
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [Hyper-V Documentation](https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/)
|
||||||
|
- [Ansible Windows Guide](https://docs.ansible.com/ansible/latest/os_guide/windows_usage.html)
|
||||||
|
- [WinRM Setup](https://docs.ansible.com/ansible/latest/os_guide/windows_setup.html)
|
||||||
|
- [Project CLAUDE.md](CLAUDE.md) - Architecture documentation
|
||||||
|
- [Provisioning Guide](playbooks/README-provision.md) - Detailed VM provisioning
|
||||||
42
playbooks/list-hyperv-switches.yml
Normal file
42
playbooks/list-hyperv-switches.yml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
# Quick playbook to list available Hyper-V virtual switches
|
||||||
|
# Usage: ansible-playbook playbooks/list-hyperv-switches.yml
|
||||||
|
|
||||||
|
- name: List Hyper-V Virtual Switches
|
||||||
|
hosts: hyperv
|
||||||
|
gather_facts: false
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Get virtual switches
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
Get-VMSwitch | Select-Object Name, SwitchType, NetAdapterInterfaceDescription | Format-Table -AutoSize
|
||||||
|
register: switches
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Display switches
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "{{ switches.stdout_lines }}"
|
||||||
|
|
||||||
|
- name: Get switches in JSON format
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
Get-VMSwitch | Select-Object Name, SwitchType | ConvertTo-Json
|
||||||
|
register: switches_json
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Parse switches
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
switch_list: "{{ switches_json.stdout | from_json }}"
|
||||||
|
|
||||||
|
- name: Display switch names
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "========================================="
|
||||||
|
- "Available Virtual Switches:"
|
||||||
|
- "========================================="
|
||||||
|
- "{{ switch_list | map(attribute='Name') | list }}"
|
||||||
|
- ""
|
||||||
|
- "To use a switch, either:"
|
||||||
|
- "1. Pass as variable: -e vm_switch='<switch_name>'"
|
||||||
|
- "2. Update default in: /home/ptoal/Dev/inventories/toallab-inventory/group_vars/hyperv/vars.yml"
|
||||||
|
- ""
|
||||||
|
- "Current default: {{ default_vm_switch }}"
|
||||||
388
playbooks/provision-hyperv-host.yml
Normal file
388
playbooks/provision-hyperv-host.yml
Normal file
@@ -0,0 +1,388 @@
|
|||||||
|
---
|
||||||
|
# Provision Hyper-V Host for Demo Environment
|
||||||
|
# This playbook configures a fresh Windows Server with Hyper-V for demonstration purposes
|
||||||
|
#
|
||||||
|
# WARNING: This is for DEMO environments only. Production deployments require:
|
||||||
|
# - Proper security hardening
|
||||||
|
# - Network segmentation
|
||||||
|
# - Storage configuration (SAN, clustering, etc.)
|
||||||
|
# - Backup and DR planning
|
||||||
|
# - Monitoring and alerting
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ansible-playbook playbooks/provision-hyperv-host.yml
|
||||||
|
|
||||||
|
- name: Provision Hyper-V Host
|
||||||
|
hosts: hyperv
|
||||||
|
gather_facts: true
|
||||||
|
|
||||||
|
vars:
|
||||||
|
|
||||||
|
# Virtual switch configuration
|
||||||
|
demo_switches:
|
||||||
|
- name: "External-NAT"
|
||||||
|
type: "External"
|
||||||
|
notes: "External switch with NAT for internet access"
|
||||||
|
- name: "Internal-Lab"
|
||||||
|
type: "Internal"
|
||||||
|
notes: "Internal switch for isolated lab network"
|
||||||
|
|
||||||
|
# NAT configuration for demo
|
||||||
|
nat_network: "192.168.100.0/24"
|
||||||
|
nat_gateway: "192.168.100.1"
|
||||||
|
nat_name: "DemoNAT"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Display provisioning plan
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "========================================="
|
||||||
|
- "Hyper-V Host Provisioning Plan"
|
||||||
|
- "========================================="
|
||||||
|
- "Host: {{ inventory_hostname }}"
|
||||||
|
- "Storage Drive: {{ vm_storage_drive }}"
|
||||||
|
- "VM Path: {{ vm_storage_path }}"
|
||||||
|
- "ISO Path: {{ iso_storage_path }}"
|
||||||
|
- "Virtual Switches: {{ demo_switches | map(attribute='name') | list | join(', ') }}"
|
||||||
|
- "NAT Network: {{ nat_network }}"
|
||||||
|
- "========================================="
|
||||||
|
|
||||||
|
- name: Check if Hyper-V is installed
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
Get-WindowsFeature -Name Hyper-V | Select-Object -ExpandProperty InstallState
|
||||||
|
register: hyperv_state
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Install Hyper-V role and management tools
|
||||||
|
ansible.windows.win_feature:
|
||||||
|
name:
|
||||||
|
- Hyper-V
|
||||||
|
- Hyper-V-PowerShell
|
||||||
|
- RSAT-Hyper-V-Tools
|
||||||
|
state: present
|
||||||
|
include_management_tools: true
|
||||||
|
register: hyperv_install
|
||||||
|
when: hyperv_state.stdout | trim != 'Installed'
|
||||||
|
|
||||||
|
- name: Reboot if Hyper-V was installed
|
||||||
|
ansible.windows.win_reboot:
|
||||||
|
reboot_timeout: 600
|
||||||
|
post_reboot_delay: 60
|
||||||
|
when:
|
||||||
|
- hyperv_install is defined
|
||||||
|
- hyperv_install.reboot_required | default(false)
|
||||||
|
|
||||||
|
- name: Wait for system to be ready after reboot
|
||||||
|
ansible.windows.win_ping:
|
||||||
|
retries: 10
|
||||||
|
delay: 30
|
||||||
|
when:
|
||||||
|
- hyperv_install is defined
|
||||||
|
- hyperv_install.reboot_required | default(false)
|
||||||
|
|
||||||
|
- name: Create storage directories
|
||||||
|
ansible.windows.win_file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: directory
|
||||||
|
loop:
|
||||||
|
- "{{ vm_storage_path }}"
|
||||||
|
- "{{ iso_storage_path }}"
|
||||||
|
- "{{ vhd_template_path }}"
|
||||||
|
|
||||||
|
- name: Set storage directory permissions
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
# Grant Hyper-V full control to storage directories
|
||||||
|
icacls "{{ item }}" /grant "NT VIRTUAL MACHINE\Virtual Machines:(OI)(CI)F"
|
||||||
|
loop:
|
||||||
|
- "{{ vm_storage_path }}"
|
||||||
|
- "{{ iso_storage_path }}"
|
||||||
|
- "{{ vhd_template_path }}"
|
||||||
|
register: acl_result
|
||||||
|
changed_when: "'Successfully processed' in acl_result.stdout"
|
||||||
|
|
||||||
|
- name: Get existing virtual switches
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
Get-VMSwitch | Select-Object Name, SwitchType | ConvertTo-Json
|
||||||
|
register: existing_switches
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Parse existing switches
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
existing_switch_names: "{{ (existing_switches.stdout | from_json | default([])) | map(attribute='Name') | list }}"
|
||||||
|
when: existing_switches.stdout | trim | length > 0
|
||||||
|
|
||||||
|
- name: Set empty switch list if none exist
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
existing_switch_names: []
|
||||||
|
when: existing_switches.stdout | trim | length == 0
|
||||||
|
|
||||||
|
- name: Display existing switches
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "Existing switches: {{ existing_switch_names | join(', ') if existing_switch_names | length > 0 else 'None' }}"
|
||||||
|
|
||||||
|
- name: Create External virtual switch
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
# Get the first network adapter that is up and has a default gateway
|
||||||
|
$adapter = Get-NetAdapter | Where-Object {
|
||||||
|
$_.Status -eq 'Up' -and
|
||||||
|
(Get-NetIPConfiguration -InterfaceIndex $_.ifIndex).IPv4DefaultGateway -ne $null
|
||||||
|
} | Select-Object -First 1
|
||||||
|
|
||||||
|
if ($adapter) {
|
||||||
|
New-VMSwitch -Name "External-NAT" `
|
||||||
|
-NetAdapterName $adapter.Name `
|
||||||
|
-AllowManagementOS $true `
|
||||||
|
-Notes "External switch for VM internet access"
|
||||||
|
Write-Host "Created External-NAT switch using adapter: $($adapter.Name)"
|
||||||
|
} else {
|
||||||
|
Write-Host "No suitable network adapter found for external switch"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
register: external_switch
|
||||||
|
when: "'External-NAT' not in existing_switch_names"
|
||||||
|
failed_when: external_switch.rc != 0
|
||||||
|
changed_when: "'Created External-NAT' in external_switch.stdout"
|
||||||
|
|
||||||
|
- name: Create Internal virtual switch
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
New-VMSwitch -Name "Internal-Lab" `
|
||||||
|
-SwitchType Internal `
|
||||||
|
-Notes "Internal switch for isolated lab network"
|
||||||
|
when: "'Internal-Lab' not in existing_switch_names"
|
||||||
|
|
||||||
|
- name: Configure NAT for internal network
|
||||||
|
block:
|
||||||
|
- name: Get Internal-Lab switch interface index
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
$adapter = Get-NetAdapter | Where-Object { $_.Name -like '*Internal-Lab*' }
|
||||||
|
$adapter.ifIndex
|
||||||
|
register: internal_adapter_index
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Configure IP address on Internal-Lab switch
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
$ifIndex = {{ internal_adapter_index.stdout | trim }}
|
||||||
|
# Remove existing IP if present
|
||||||
|
Remove-NetIPAddress -InterfaceIndex $ifIndex -Confirm:$false -ErrorAction SilentlyContinue
|
||||||
|
# Add new IP address
|
||||||
|
New-NetIPAddress -InterfaceIndex $ifIndex `
|
||||||
|
-IPAddress {{ nat_gateway }} `
|
||||||
|
-PrefixLength 24 `
|
||||||
|
-ErrorAction Stop
|
||||||
|
register: ip_config
|
||||||
|
changed_when: true
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Check if NAT already exists
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
Get-NetNat -Name "{{ nat_name }}" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name
|
||||||
|
register: existing_nat
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Create NAT for internal network
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
New-NetNat -Name "{{ nat_name }}" `
|
||||||
|
-InternalIPInterfaceAddressPrefix "{{ nat_network }}"
|
||||||
|
when: existing_nat.stdout | trim | length == 0
|
||||||
|
|
||||||
|
- name: Configure WinRM for Ansible
|
||||||
|
block:
|
||||||
|
- name: Enable WinRM HTTPS listener
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
# Check if HTTPS listener exists
|
||||||
|
$httpsListener = Get-ChildItem WSMan:\localhost\Listener |
|
||||||
|
Where-Object { $_.Keys -contains 'Transport=HTTPS' }
|
||||||
|
|
||||||
|
if (-not $httpsListener) {
|
||||||
|
# Create self-signed certificate
|
||||||
|
$cert = New-SelfSignedCertificate -DnsName $env:COMPUTERNAME `
|
||||||
|
-CertStoreLocation Cert:\LocalMachine\My `
|
||||||
|
-NotAfter (Get-Date).AddYears(5)
|
||||||
|
|
||||||
|
# Create HTTPS listener
|
||||||
|
New-Item -Path WSMan:\localhost\Listener `
|
||||||
|
-Transport HTTPS `
|
||||||
|
-Address * `
|
||||||
|
-CertificateThumbPrint $cert.Thumbprint -Force
|
||||||
|
|
||||||
|
Write-Host "Created HTTPS listener"
|
||||||
|
} else {
|
||||||
|
Write-Host "HTTPS listener already exists"
|
||||||
|
}
|
||||||
|
register: winrm_https
|
||||||
|
changed_when: "'Created HTTPS listener' in winrm_https.stdout"
|
||||||
|
|
||||||
|
- name: Configure WinRM service settings
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
# Set WinRM service to start automatically
|
||||||
|
Set-Service WinRM -StartupType Automatic
|
||||||
|
|
||||||
|
# Configure WinRM settings for Ansible
|
||||||
|
Set-Item WSMan:\localhost\Service\Auth\Basic -Value $true
|
||||||
|
Set-Item WSMan:\localhost\Service\Auth\CredSSP -Value $true
|
||||||
|
Set-Item WSMan:\localhost\Service\AllowUnencrypted -Value $false
|
||||||
|
Set-Item WSMan:\localhost\MaxTimeoutms -Value 1800000
|
||||||
|
|
||||||
|
# Restart WinRM
|
||||||
|
Restart-Service WinRM
|
||||||
|
register: winrm_config
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
|
- name: Configure Windows Firewall for demo
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
# Enable WinRM firewall rules
|
||||||
|
Enable-NetFirewallRule -Name "WINRM-HTTP-In-TCP-PUBLIC"
|
||||||
|
Enable-NetFirewallRule -Name "WINRM-HTTPS-In-TCP-PUBLIC"
|
||||||
|
|
||||||
|
# Enable RDP for management
|
||||||
|
Enable-NetFirewallRule -Name "RemoteDesktop-UserMode-In-TCP"
|
||||||
|
|
||||||
|
# Enable Hyper-V management
|
||||||
|
Enable-NetFirewallRule -DisplayGroup "Hyper-V*"
|
||||||
|
register: firewall_config
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
|
- name: Set Hyper-V default VM storage locations
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
Set-VMHost -VirtualHardDiskPath "{{ vm_storage_path }}" `
|
||||||
|
-VirtualMachinePath "{{ vm_storage_path }}"
|
||||||
|
register: vm_defaults
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
|
- name: Enable Hyper-V Enhanced Session Mode
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
Set-VMHost -EnableEnhancedSessionMode $true
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
|
- name: Configure power settings for demo
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
# Set to High Performance power plan
|
||||||
|
powercfg /setactive 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c
|
||||||
|
|
||||||
|
# Disable sleep and hibernation
|
||||||
|
powercfg /change monitor-timeout-ac 0
|
||||||
|
powercfg /change disk-timeout-ac 0
|
||||||
|
powercfg /change standby-timeout-ac 0
|
||||||
|
powercfg /hibernate off
|
||||||
|
register: power_config
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
|
- name: Install helpful PowerShell modules
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
# Install PowerShell modules for Hyper-V management
|
||||||
|
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
# Install modules if not present
|
||||||
|
$modules = @('Posh-SSH', 'PSWindowsUpdate')
|
||||||
|
foreach ($module in $modules) {
|
||||||
|
if (-not (Get-Module -ListAvailable -Name $module)) {
|
||||||
|
Install-Module -Name $module -Force -SkipPublisherCheck -Scope AllUsers
|
||||||
|
Write-Host "Installed $module"
|
||||||
|
} else {
|
||||||
|
Write-Host "$module already installed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
register: ps_modules
|
||||||
|
changed_when: "'Installed' in ps_modules.stdout"
|
||||||
|
|
||||||
|
- name: Create README file on Hyper-V host
|
||||||
|
ansible.windows.win_copy:
|
||||||
|
content: |
|
||||||
|
HYPER-V DEMO HOST CONFIGURATION
|
||||||
|
================================
|
||||||
|
|
||||||
|
This host has been configured by Ansible for demonstration purposes.
|
||||||
|
|
||||||
|
Storage Locations:
|
||||||
|
- VMs: {{ vm_storage_path }}
|
||||||
|
- ISOs: {{ iso_storage_path }}
|
||||||
|
- Templates: {{ vhd_template_path }}
|
||||||
|
|
||||||
|
Virtual Switches:
|
||||||
|
{% for switch in demo_switches %}
|
||||||
|
- {{ switch.name }} ({{ switch.type }}): {{ switch.notes }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
NAT Configuration:
|
||||||
|
- Network: {{ nat_network }}
|
||||||
|
- Gateway: {{ nat_gateway }}
|
||||||
|
- NAT Name: {{ nat_name }}
|
||||||
|
|
||||||
|
Management:
|
||||||
|
- WinRM: Enabled (HTTP: 5985, HTTPS: 5986)
|
||||||
|
- RDP: Enabled
|
||||||
|
- Enhanced Session Mode: Enabled
|
||||||
|
|
||||||
|
Next Steps:
|
||||||
|
1. Copy Windows Server ISOs to {{ iso_storage_path }}
|
||||||
|
2. Use Ansible to provision VMs: ansible-playbook provision-vm.yml
|
||||||
|
3. Access Hyper-V Manager remotely or via RDP
|
||||||
|
|
||||||
|
WARNING: This is a DEMO configuration - not suitable for production!
|
||||||
|
- Firewall rules are permissive
|
||||||
|
- Self-signed certificates for WinRM
|
||||||
|
- No backup/DR configuration
|
||||||
|
- No monitoring configured
|
||||||
|
|
||||||
|
Managed by: Ansible Automation Platform
|
||||||
|
Provisioned: {{ ansible_date_time.iso8601 }}
|
||||||
|
dest: "{{ vm_storage_drive }}\\HYPERV-README.txt"
|
||||||
|
|
||||||
|
- name: Get Hyper-V host information
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
$host = Get-VMHost
|
||||||
|
[PSCustomObject]@{
|
||||||
|
ComputerName = $host.ComputerName
|
||||||
|
LogicalProcessorCount = $host.LogicalProcessorCount
|
||||||
|
MemoryCapacity = [math]::Round($host.MemoryCapacity / 1GB, 2)
|
||||||
|
VirtualHardDiskPath = $host.VirtualHardDiskPath
|
||||||
|
VirtualMachinePath = $host.VirtualMachinePath
|
||||||
|
MacAddressMinimum = $host.MacAddressMinimum
|
||||||
|
MacAddressMaximum = $host.MacAddressMaximum
|
||||||
|
EnhancedSessionModeEnabled = $host.EnableEnhancedSessionMode
|
||||||
|
} | ConvertTo-Json
|
||||||
|
register: hyperv_info
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Display Hyper-V configuration summary
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "========================================="
|
||||||
|
- "Hyper-V Host Provisioning Complete"
|
||||||
|
- "========================================="
|
||||||
|
- "{{ hyperv_info.stdout | from_json | to_nice_json }}"
|
||||||
|
- ""
|
||||||
|
- "Virtual Switches Created:"
|
||||||
|
- "{{ demo_switches | map(attribute='name') | list }}"
|
||||||
|
- ""
|
||||||
|
- "Storage Configured:"
|
||||||
|
- " VMs: {{ vm_storage_path }}"
|
||||||
|
- " ISOs: {{ iso_storage_path }}"
|
||||||
|
- " Templates: {{ vhd_template_path }}"
|
||||||
|
- ""
|
||||||
|
- "NAT Network:"
|
||||||
|
- " Network: {{ nat_network }}"
|
||||||
|
- " Gateway: {{ nat_gateway }}"
|
||||||
|
- ""
|
||||||
|
- "Next Steps:"
|
||||||
|
- "1. Upload Windows Server ISO to {{ iso_storage_path }}"
|
||||||
|
- "2. Update group_vars/hyperv/vars.yml with:"
|
||||||
|
- " - windows_server_iso: {{ iso_storage_path }}\\Windows_Server_2022.iso"
|
||||||
|
- " - default_vm_switch: External-NAT or Internal-Lab"
|
||||||
|
- "3. Run: ansible-playbook playbooks/list-hyperv-switches.yml"
|
||||||
|
- "4. Provision VMs: ansible-playbook playbooks/provision-vm.yml"
|
||||||
|
- ""
|
||||||
|
- "WARNING: Demo configuration - not production ready!"
|
||||||
|
- "========================================="
|
||||||
|
|
||||||
|
- name: Update inventory group_vars with discovered configuration
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "Update /home/ptoal/Dev/inventories/toallab-inventory/group_vars/hyperv/vars.yml:"
|
||||||
|
- ""
|
||||||
|
- "vm_storage_path: '{{ vm_storage_path }}'"
|
||||||
|
- "iso_storage_path: '{{ iso_storage_path }}'"
|
||||||
|
- "default_vm_switch: 'External-NAT' # or 'Internal-Lab'"
|
||||||
|
delegate_to: localhost
|
||||||
Reference in New Issue
Block a user