I had to run up a bucket load of build servers on a HyperV environment the other day so decide to automate the process a little.
I have also started using RAM disks for the agents too, to speed them up.
Step for the build server load were:
- Install OS (Win Server 2012 R2)
- Install SQL 2012 Express
- Install VS 2015
- Install build agent and point it at the TC Build agent on Drive E:
- Install RAMDisk (here)
- Run windows updates
If you are unsure where to get the Build Agent installer from click the agents tab in TeamCity and there is a link in the top left.
At this point move the build agent to the RAM disk by:
- Stop Team City Build agent service
- Change the drive letter of E: to F:
- create a RAM drive as E: with image file on F and save to disk on shutdown
- I used 5Gb drive and it handles ok
- Copy the build agent folder from F to E
- Start Build agent service
After this i also installed IIS and disabled the firewall
Lastly sysprep the server and check OOBE/Shutdown
Then delete the VM I was using and the VHDX file that was generated keep for using as a template, I backed it up to use on other hosts as well in future.
Then wrote the below script that run’s on the host, it does the following for me:
- Creates a new VHDX using a diff based on the original (makes run up really fast)
- Creates a VM that uses that VHDX
- Sets some values that I can set in the New-VM Command
- Changes the network config of the guest
param( [string]$MachineName="MyMachine", [string]$IPv4Address="10.0.0.20" ) Function Set-VMNetworkConfiguration { [CmdletBinding()] Param ( [Parameter(Mandatory=$true, Position=1, ParameterSetName='DHCP', ValueFromPipeline=$true)] [Parameter(Mandatory=$true, Position=0, ParameterSetName='Static', ValueFromPipeline=$true)] [Microsoft.HyperV.PowerShell.VMNetworkAdapter]$NetworkAdapter, [Parameter(Mandatory=$true, Position=1, ParameterSetName='Static')] [String[]]$IPAddress=@(), [Parameter(Mandatory=$false, Position=2, ParameterSetName='Static')] [String[]]$Subnet=@(), [Parameter(Mandatory=$false, Position=3, ParameterSetName='Static')] [String[]]$DefaultGateway = @(), [Parameter(Mandatory=$false, Position=4, ParameterSetName='Static')] [String[]]$DNSServer = @(), [Parameter(Mandatory=$false, Position=0, ParameterSetName='DHCP')] [Switch]$Dhcp ) $VM = Get-WmiObject -Namespace 'root\virtualization\v2' -Class 'Msvm_ComputerSystem' | Where-Object { $_.ElementName -eq $NetworkAdapter.VMName } $VMSettings = $vm.GetRelated('Msvm_VirtualSystemSettingData') | Where-Object { $_.VirtualSystemType -eq 'Microsoft:Hyper-V:System:Realized' } $VMNetAdapters = $VMSettings.GetRelated('Msvm_SyntheticEthernetPortSettingData') $NetworkSettings = @() foreach ($NetAdapter in $VMNetAdapters) { if ($NetAdapter.Address -eq $NetworkAdapter.MacAddress) { $NetworkSettings = $NetworkSettings + $NetAdapter.GetRelated("Msvm_GuestNetworkAdapterConfiguration") } } $NetworkSettings[0].IPAddresses = $IPAddress $NetworkSettings[0].Subnets = $Subnet $NetworkSettings[0].DefaultGateways = $DefaultGateway $NetworkSettings[0].DNSServers = $DNSServer $NetworkSettings[0].ProtocolIFType = 4096 if ($dhcp) { $NetworkSettings[0].DHCPEnabled = $true } else { $NetworkSettings[0].DHCPEnabled = $false } $Service = Get-WmiObject -Class "Msvm_VirtualSystemManagementService" -Namespace "root\virtualization\v2" $setIP = $Service.SetGuestNetworkAdapterConfiguration($VM, $NetworkSettings[0].GetText(1)) if ($setip.ReturnValue -eq 4096) { $job=[WMI]$setip.job while ($job.JobState -eq 3 -or $job.JobState -eq 4) { start-sleep 1 $job=[WMI]$setip.job } if ($job.JobState -eq 7) { write-host "Success" } else { $job.GetError() } } elseif($setip.ReturnValue -eq 0) { Write-Host "Success" } } Write-Host $MachineName New-VHD –Path “D:\Hyper-V\Diff.$MachineName.vhdx” –ParentPath “D:\Hyper-V\agent_template2.VHDX” –Differencing New-VM -Name $MachineName -MemoryStartupBytes 6024000000 -Generation 2 -BootDevice VHD -VHDPath “D:\Hyper-V\Diff.$MachineName.vhdx” -SwitchName "10.0.0.xx" Set-VM -Name $MachineName -DynamicMemory -ProcessorCount 8 Write-Host $IPv4Address Get-VMNetworkAdapter -VMName $MachineName -Name "Network Adapter" | Set-VMNetworkConfiguration -IPAddress $IPv4Address -Subnet 255.255.255.0 -DNSServer 10.0.0.15 -DefaultGateway 10.0.0.1
The above command takes a parameter of the new machine name and the IP you want to give the server, I have hard codded the subnet, gateway and DNS, but these should probably be made parameters too, depending on your environment.
After this i just have to login to the agents and domain join them after they are spun up. I used to use VMM that would domain join out-of-the-box, but it looked painful to do from a script on the host so have left it.
An also authorize them on the TeamCity server.