This commit is contained in:
2026-04-29 09:52:02 -04:00
parent fe11468547
commit a096a7eaa0
4 changed files with 1333 additions and 0 deletions

509
HANDOFF.md Normal file
View 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
View 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

View 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 }}"

View 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