I struggle to (dynamically) start the Jenkins slave agent from my dedicated slave machine (Windows 2012 R2 server). The Jenkins master (ver. 1.617 - which I can upgrade if necessary, but not downgrade [before ver. 1.498 no credentials were required]) is on a Windows 2012 R2 server. Security is enabled and configured via the Active Directory plugin and Project-based Matrix Authorization Strategy.
Because of the Active Directory involved, I cannot simply add a system user to authenticate with (via -jnlpCredentials username:password
or -jnlpCredentials username:apitoken
). As a workaround I am using my Jenkins service user for that, but I don't like it's API-Token lying around hard-coded in the script.
I am trying to use the alternative -secret secretKey
, but that secretKey is randomly created when a slave node is registered on the master.
Since I am using the Azure Slave Plugin, the slave nodes and the associated virtual machines are created for me.
The virtual machines are created from a pre-defined image, that I can change in whatever way necessary.
In this pre-defined image I have a PowerShell script executed on start-up. It is derived from the sample given here. It doesn't have to be PowerShell, any other way would be okay as well.
Set-ExecutionPolicy Unrestricted
# base url to Jenkins master
$jenkinsserverurl = "https://jenkins.mycompany.com/"
# the azure-slave-plugin is creating VMs with names like 'Azure0807150842'
$vmname = (Get-Culture).TextInfo.ToTitleCase($env:computername.tolower())
# authenticate with Jenkins service user + API-token - since we don't know the '-secret'
$apiToken="jenkins_user:1234abcdefab56c7d890de1f2a345b67"
Write-Output "Downloading jenkins slave jar "
# in order to avoid updating it manually for Jenkins master updates
$slaveJarSource = $jenkinsserverurl + "jnlpJars/slave.jar"
$slaveJarLocal = "C:\jenkins_home\slave.jar"
$wc = New-Object System.Net.WebClient
$wc.DownloadFile($slaveJarSource, $slaveJarLocal)
Write-Output "Executing slave process "
$jnlpSource = $jenkinsserverurl+"computer/" + $vmname + "/slave-agent.jnlp"
# expect java.exe in the PATH, and use -noCertificateCheck to skip SSL validation
& java -jar $slaveJarLocal -jnlpCredentials $apiToken -jnlpUrl $jnlpSource -noCertificateCheck
Downloading the JNLP file and reading the contained secret is no option, since I need proper HTTP authentication at the Jenkins master for that as well.
Write-Output "Downloading jenkins slave jnlp "
$jnlpSource = $jenkinsserverurl+"computer/" + $vmname + "/slave-agent.jnlp"
$jnlpLocal = "C:\jenkins_home\slave-agent.jnlp"
$wc = New-Object System.Net.WebClient
$wc.DownloadFile($jnlpSource, $jnlpLocal)
Write-Output "Extracting secret from jenkins slave jnlp "
[xml]$jnlpFile = Get-Content $jnlpLocal
# the first argument in the generated JNLP contains the secret
$secret = Select-Xml "//jnlp/application-desc/argument[1]/text()" $jnlpFile
In an ideal world, the plugin creating the slave node and its VM, would login to the created VM and execute a script similar to the one in my question - with the addition of the injected Jenkins server url, VM name, and generated secret. Since that is not the case for the current Azure Slave Plugin version, I am stuck with my workaround script - using my existing Jenkins service user.
I use this to let the plugin create a bigger/faster VM on the fly, which is only used for a daily test run and is shutdown automatically the rest of the time (and therefore causes no costs when unused). If someone is interested, that is the setup I ended up with:
Generalized Azure VM Image (Windows 2012 R2, with JDK, Maven, Git installed). Via NSSM, I installed the PowerShell script (which starts the slave agent) as a Windows Service, to be executed automatically at machine bootup (same as in the question):
Set-ExecutionPolicy Unrestricted
# base url to Jenkins master
$jenkinsserverurl = "https://jenkins.mycompany.com/"
# the azure-slave-plugin is creating VMs with names like 'Azure0807150842'
$vmname = (Get-Culture).TextInfo.ToTitleCase($env:computername.tolower())
# authenticate with Jenkins service user + API-token - since we don't know the '-secret'
$apiToken="jenkins_user:1234abcdefab56c7d890de1f2a345b67"
Write-Output "Downloading jenkins slave jar "
# in order to avoid updating it manually for Jenkins master updates
$slaveJarSource = $jenkinsserverurl + "jnlpJars/slave.jar"
$slaveJarLocal = "C:\jenkins_home\slave.jar"
$wc = New-Object System.Net.WebClient
$wc.DownloadFile($slaveJarSource, $slaveJarLocal)
Write-Output "Executing slave process "
$jnlpSource = $jenkinsserverurl+"computer/" + $vmname + "/slave-agent.jnlp"
# expect java.exe in the PATH, and use -noCertificateCheck to skip SSL validation
& java -jar $slaveJarLocal -jnlpCredentials $apiToken -jnlpUrl $jnlpSource -noCertificateCheck
Jenkins master with Azure Slave Plugin installed and configured to use this VM image, with shutdown-on-idle after five minutes.
Jenkins Maven Project (Job) that is configured to only run on a Azure slave node, checks out my test project from Git, and executes the jUnit Selenium tests from there.