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

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