Running up TeamCity build Agents on HyperV

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:

  1. Install OS (Win Server 2012 R2)
  2. Install SQL 2012 Express
  3. Install VS 2015
  4. Install build agent and point it at the TC Build agent on Drive E:
  5. Install RAMDisk (here)
  6. 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.

InstallTeamCityBuildAgent

At this point move the build agent to the RAM disk by:

  1. Stop Team City Build agent service
  2. Change the drive letter of E: to F:
  3. create a RAM drive as E: with image file on F and save to disk on shutdown
    1. I used 5Gb drive and it handles ok
  4. Copy the build agent folder from F to E
  5. 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:

  1. Creates a new VHDX using a diff based on the original (makes run up really fast)
  2. Creates a VM that uses that VHDX
  3. Sets some values that I can set in the New-VM Command
  4. 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.

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s