New-LabVM.psm1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# Version 2.4 (27-03-2017)

<#
    .Synopsis
    This module lets you roll out your lab environment automatically. This is the second step: creating a Lab VM's.

    .DESCRIPTION
    This module lets you roll out your lab environment automatically as described in our blogs (www.365dude.nl and www.svenvanrijen.nl).
    This is the second step: creating Lab VM's.
    First step: creating a NATSwitch (New-NATSwitch).

    New-LabVM generates a new VM in the lab environment.
    IPAddress is a mandatory parameter and must be in the "172.16.10.10/24" notation.
    Please use the same address space as used when creating the NATSwitch.
    The specification of a Gateway is optional. If DNS-server is not specified, the default will be used. (172.16.10.1)
    The specification of a DNS-server is optional. If DNS-server is not specified, the default will be used. (8.8.8.8)

    For more information or help regarding this module, please visit the GitHub repo: https://github.com/ralpje/PowerShell-Lab-Module

    .EXAMPLE
    New-LabVM -VMName Test -VMIP 10.0.0.18/29 -GWIP 10.0.0.1

    .EXAMPLE
    New-LabVM -VMName Hostname -VMIP 192.168.123.20/24 -DNSIP 192.168.123.5 -GWIP 192.168.123.1
#>

function New-LabVM
{
  [CmdletBinding(
    SupportsShouldProcess = $true
  )]
  [OutputType([String])]
  Param
  ( 
    [Parameter(Mandatory = $true)]
    [string]$VMName,
    
    [Parameter(Mandatory = $false)]
    [int64]$MemoryStartupBytes = 2048MB,
        
    [Parameter(Mandatory = $true)]
    [string]$VMIP,
    
    [Parameter(Mandatory = $false)]
    [string]$GWIP = '172.16.10.1',

    [Parameter(Mandatory = $true)]
    [string]$Diskpath,

    [Parameter(Mandatory = $true)]
    [string]$ParentDisk,

    [Parameter(Mandatory = $true)]
    [string]$VMSwitch,
        
    [Parameter(Mandatory = $false)]
    [string]$DNSIP = '8.8.8.8',
             
    [Parameter(Mandatory = $false)]
    [string]$Unattendloc = 'https://raw.githubusercontent.com/ralpje/PowerShell-Lab-Module/master/Templates/unattend.xml',
    
    [Parameter(Mandatory = $false)]
    [boolean]$DSC = $false,

    [Parameter(Mandatory = $false)]
    [string]$DSCPullConfig,
    
    [Parameter(Mandatory = $false)]
    [boolean]$NestedVirt 
  )

  Begin
  {
  }
  Process
  {
    #region create diff disk
    # Create new differencing disk
    
    $maindiskpath = "$diskpath$VMName.vhdx"
    
    New-VHD -ParentPath $ParentDisk -Path $maindiskpath -Differencing
    
    Write-Verbose -Message "Created a differencing disk $maindiskpath."
    
    Start-Sleep -Seconds 2
    #endregion
  
    #region copy adjusted answer file
    # mount VHD
    $VHD = Mount-VHD -Path $maindiskpath -Passthru

    $x = Get-Disk $vhd.Number |
      Get-Partition |
      Where-Object -FilterScript {
        $_.Type -eq 'Basic'
    }

    Get-Disk $vhd.Number | Set-Disk -IsOffline $false

    Set-Partition -PartitionNumber $x.PartitionNumber -NewDriveLetter T -DiskNumber $x.DiskNumber -ErrorAction SilentlyContinue 

    New-PSDrive -Name T -Root t:\ -PSProvider FileSystem | Out-Null
    
    Write-Verbose -Message 'Differencing disk mounted'
    
    Start-Sleep -Seconds 2
    
    # Copy xml from source to local temp dir
    New-Item -ItemType Directory -Path "$PSScriptRoot\Temp" -Force
    $output = "$PSScriptRoot\temp\unattended.xml"
    Invoke-WebRequest -Uri $unattendloc -OutFile $output
    
    Write-Verbose -Message "Unattended.xml copied from $unattendloc to $PSScriptRoot\Temp."
    
    Start-Sleep -Seconds 2
    
    $SourceXML = "$PSScriptRoot\temp\unattended.xml"
    
    # add XML Content
    [xml]$Unattend = Get-Content $SourceXML
    $Unattend.unattend.settings[1].component[2].ComputerName = $VMName
    $Unattend.unattend.settings[1].component[3].Interfaces.Interface.UnicastIpAddresses.IpAddress.'#text' = $VMIP
    $Unattend.unattend.settings[1].component[4].Interfaces.Interface.DNSServerSearchOrder.IpAddress.'#Text' = $DNSIP
    $Unattend.unattend.settings[1].component[3].Interfaces.Interface.Routes.Route.NextHopAddress = $GWIP
    $Unattend.Save("T:\\Unattend.xml")
    
    Write-Verbose -Message "Set VM name to $VMName, IP Address to $VMIP, DNS IP to $DNSIP and GW IP to $GWIP"    
    
    Start-Sleep -Seconds 2
    
    if ($DSC -eq $true)
    {
      # Copy DSC Pull Config to TEMP
      $destps1 = "$PSScriptRoot\temp\DSCPullConfig.ps1"
      Copy-Item -Path $DSCPullConfig -Destination $destps1
      Set-Location -Path "$PSScriptRoot\temp\"
        
      #Edit DSC Pull Config so it will contain the right node-name ($vmname)
      (Get-Content -Path '.\DSCPullConfig.ps1') -replace '\breplace\b', "$VMName" | Out-File -FilePath '.\DSCPullConfig.ps1'
        
      #Kick off DSC Pull Config to generate metamof
      . '.\DSCPullConfig.ps1'
      DscMetaConfigs @Params
             
      $sourcemof = "$PSScriptRoot\temp\DscMetaConfigs\$VMName.meta.mof"
      $destmof = "T:\Windows\system32\Configuration\MetaConfig.mof"
      Move-Item -Path $sourcemof -Destination $destmof -Force

      Write-Verbose -Message "Copied metaconfig.mof to T:\Windows\system32\Configuration\MetaConfig.mof"
      
      reg.exe load HKLM\Vhd T:\Windows\System32\Config\Software
      Set-Location -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies
      Set-ItemProperty -Path . -Name DSCAutomationHostEnabled -Value 2
      [gc]::Collect()
      reg.exe unload HKLM\Vhd
        
      Write-Verbose -Message "Enabled DSC Automation Host on $VMName."
    }
    
    # dismount VHD
    Dismount-VHD $maindiskpath

    Write-Verbose -Message 'Differencing disk dismounted'
    #endregion
  
    #region create new vm
    # Create VM with new VHD
    New-VM -Name $VMName -MemoryStartupBytes $MemoryStartupBytes -SwitchName $VMSwitch -VHDPath $maindiskpath -Generation 2 -BootDevice VHD
    #endregion
    
    Write-Verbose -Message "Created new lab VM with name $VMName, $MemoryStartupBytes memory. The VM is connected to $VMSwitch with IP address $VMIP."
    
    Start-Sleep -Seconds 2
    
    if ($NestedVirt -eq $true)
    {
      Set-VMProcessor -VMName $VMName -ExposeVirtualizationExtensions $true
    
      Write-Verbose -Message "Exposed Virtualization Extensions on $VMName."
    }
  
    #region start new vm and connect to console
    # Start VM
    Start-VM $VMName
    Start-Process -FilePath vmconnect -ArgumentList localhost, $VMName

    Write-Verbose -Message "Started $VMName."

    #endregion
    
    Set-Location -Path c:\                
  }
  End
  {
  }
}