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.
