Install Jenkins using DSC – Part 2
In my previous post, I showed how to create a PowerShell script that would install a Jenkins CI Master server onto a Windows Server Core installation. The obvious next step for such a script was to convert it into a DSC configuration file.
In this post, I’m assuming WMF 5.0 is installed onto the server that will be converted into a Jenkins Master. You could manage this without WMF 5.0, but you’d need to manually install the DSC Resource modules that the configuration will use.
Once again, the full DSC Configuration script can be found at the end of the post.
Requirements
You’ll need:
- A physical or virtual machine running Windows Server 2012 R2 Core (or Full)—it should be a completely clean install with WMF 5.0 installed on it.
- An administrator login to the server.
- An internet connection to the server.
Resource Modules
This DSC Configuration requires the use of three DSC Resources:
- cChoco - This community resource is used to install Chocolatey and Jenkins.
- xNetworking - This resource is used to configure the networking on the server if required.
- PSDesiredStateConfiguration - This resource comes with PowerShell by default and is used to provide the Script resource.
The easiest way to install these resource modules is by executing these commands on the Jenkins server:
# Make sure the DSC Resource modules are downloaded
Install-Module -Name cChoco -Force
Install-Module -Name xNetworking -Force
However, if you’re using a Pull server or compiling the DSC MOF on a development machine (rather than the Jenkins node), you would need to use other methods of ensuring the modules are available.
The Configuration Components
The DSC Configuration needs to do the following things:
- Configure Networking (optional)
- Install .NET 3.5 Framework
- Install Chocolatey
- Install JDK 8
- Install Jenkins
- Configure Jenkins Port (optional)
Configure Networking
I like to use the xNetworking DSC resource to configure the IPv4 and IPv6 settings on the network adapter to have a static configuration. However, you won’t need to do this if you’re using DHCP or manual configuration. Note, in my case, my adapter was called “Ethernet”.
xIPAddress IPv4_1 {
InterfaceAlias = 'Ethernet'
AddressFamily = 'IPv4'
IPAddress = '192.168.128.20'
SubnetMask = '24'
}
xDefaultGatewayAddress IPv4G_1 {
InterfaceAlias = 'Ethernet'
AddressFamily = 'IPv4'
Address = '192.168.128.19'
}
xDnsServerAddress IPv4D_1 {
InterfaceAlias = 'Ethernet'
AddressFamily = 'IPv4'
Address = '192.168.128.10'
}
xIPAddress IPv6_1 {
InterfaceAlias = 'Ethernet'
AddressFamily = 'IPv6'
IPAddress = 'fd53:ccc5:895a:bc00::14'
SubnetMask = '64'
}
xDefaultGatewayAddress IPv6G_1 {
InterfaceAlias = 'Ethernet'
AddressFamily = 'IPv6'
Address = 'fd53:ccc5:895a:bc00::13'
}
xDnsServerAddress IPv6D_1 {
InterfaceAlias = 'Ethernet'
AddressFamily = 'IPv6'
Address = 'fd53:ccc5:895a:bc00::a'
}
Install .NET 3.5 Framework
Jenkins requires the .NET 3.5 Framework, so I’m going to use the WindowsFeature DSC Resource to install it:
WindowsFeature NetFrameworkCore {
Ensure = "Present"
Name = "NET-Framework-Core"
}
Install Chocolatey
Next up, I’m going to use the cChocoInstaller resource in the cChoco resource module (available on PowerShell Gallery here) to install the Chocolatey package manager:
cChocoInstaller installChoco {
InstallDir = "c:\choco"
DependsOn = "[WindowsFeature]NetFrameworkCore"
}
Install JDK 8 and Jenkins
The cChocoPackageInstaller resource module is then used to install JDK 8 and Jenkins:
# Install JDK8
cChocoPackageInstaller installJdk8 {
Name = "jdk8"
DependsOn = "[cChocoInstaller]installChoco"
}
# Install Jenkins
cChocoPackageInstaller installJenkins {
Name = "Jenkins"
DependsOn = "[cChocoInstaller]installChoco"
}
Configure Jenkins Port
The last step of the configuration is optional. By default, Jenkins is configured to listen on port 8080; however, I want to change it to 80. So this next part uses the Script resource to change the --httpPort
setting in the Jenkins.xml file. I use Regex to do this:
Script SetJenkinsPort {
SetScript = {
Write-Verbose -Verbose "Setting Jenkins Port to $Using:JenkinsPort"
$Config = Get-Content `
-Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml"
$NewConfig = $Config `
-replace '--httpPort=[0-9]*\s',"--httpPort=$Using:JenkinsPort "
Set-Content `
-Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" `
-Value $NewConfig `
-Force
Write-Verbose -Verbose "Restarting Jenkins"
Restart-Service `
-Name Jenkins
}
GetScript = {
$Config = Get-Content `
-Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml"
$Matches = @([regex]::matches($Config, "--httpPort=([0-9]*)\s", 'IgnoreCase'))
$CurrentPort = $Matches.Groups[1].Value
Return @{
'JenkinsPort' = $CurrentPort
}
}
TestScript = {
$Config = Get-Content `
-Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml"
$Matches = @([regex]::matches($Config, "--httpPort=([0-9]*)\s", 'IgnoreCase'))
$CurrentPort = $Matches.Groups[1].Value
If ($Using:JenkinsPort -ne $CurrentPort) {
# Jenkins port must be changed
Return $False
}
# Jenkins is already on correct port
Return $True
}
DependsOn = "[cChocoPackageInstaller]installJenkins"
}
Create the MOF
The final thing to do is download the cChoco and xNetworking DSC Resources, create the MOF, and then ask the LCM to apply it:
$ConfigData = @{
AllNodes =
@(
@{
NodeName = "LocalHost"
}
)
}
JENKINS_CI -JenkinsPort 80 -ConfigurationData $ConfigData
Start-DscConfiguration -Path .\JENKINS_CI -Wait -Verbose
The Complete DSC Configuration
Here is the complete DSC Configuration file. You just need to copy it to the server and run it. It will compile the configuration into a MOF and tell the LCM to apply it. Just remember to ensure required DSC Resource modules are installed.
Configuration JENKINS_CI {
param (
$JenkinsPort = 8080
)
Import-DscResource -ModuleName 'PSDesiredStateConfiguration'
Import-DscResource -ModuleName 'cChoco'
Import-DscResource -ModuleName 'xNetworking'
Node $AllNodes.NodeName {
# Configure networking (optional)
xIPAddress IPv4_1 {
InterfaceAlias = 'Ethernet'
AddressFamily = 'IPv4'
IPAddress = '192.168.128.20'
SubnetMask = '24'
}
xDefaultGatewayAddress IPv4G_1 {
InterfaceAlias = 'Ethernet'
AddressFamily = 'IPv4'
Address = '192.168.128.19'
}
xDnsServerAddress IPv4D_1 {
InterfaceAlias = 'Ethernet'
AddressFamily = 'IPv4'
Address = '192.168.128.10'
}
xIPAddress IPv6_1 {
InterfaceAlias = 'Ethernet'
AddressFamily = 'IPv6'
IPAddress = 'fd53:ccc5:895a:bc00::14'
SubnetMask = '64'
}
xDefaultGatewayAddress IPv6G_1 {
InterfaceAlias = 'Ethernet'
AddressFamily = 'IPv6'
Address = 'fd53:ccc5:895a:bc00::13'
}
xDnsServerAddress IPv6D_1 {
InterfaceAlias = 'Ethernet'
AddressFamily = 'IPv6'
Address = 'fd53:ccc5:895a:bc00::a'
}
# Install .NET 3.5
WindowsFeature NetFrameworkCore {
Ensure = "Present"
Name = "NET-Framework-Core"
}
# Install Chocolatey
cChocoInstaller installChoco {
InstallDir = "c:\choco"
DependsOn = "[WindowsFeature]NetFrameworkCore"
}
# Install JDK8
cChocoPackageInstaller installJdk8 {
Name = "jdk8"
DependsOn = "[cChocoInstaller]installChoco"
}
# Install Jenkins
cChocoPackageInstaller installJenkins {
Name = "Jenkins"
DependsOn = "[cChocoInstaller]installChoco"
}
# Set the Jenkins Port
Script SetJenkinsPort {
SetScript = {
Write-Verbose -Verbose "Setting Jenkins Port to $Using:JenkinsPort"
$Config = Get-Content `
-Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml"
$NewConfig = $Config `
-replace '--httpPort=[0-9]*\s',"--httpPort=$Using:JenkinsPort "
Set-Content `
-Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" `
-Value $NewConfig `
-Force
Write-Verbose -Verbose "Restarting Jenkins"
Restart-Service `
-Name Jenkins
}
GetScript = {
$Config = Get-Content `
-Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml"
$Matches = @([regex]::matches($Config, "--httpPort=([0-9]*)\s", 'IgnoreCase'))
$CurrentPort = $Matches.Groups[1].Value
Return @{
'JenkinsPort' = $CurrentPort
}
}
TestScript = {
$Config = Get-Content `
-Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml"
$Matches = @([regex]::matches($Config, "--httpPort=([0-9]*)\s", 'IgnoreCase'))
$CurrentPort = $Matches.Groups[1].Value
If ($Using:JenkinsPort -ne $CurrentPort) {
# Jenkins port must be changed
Return $False
}
# Jenkins is already on correct port
Return $True
}
DependsOn = "[cChocoPackageInstaller]installJenkins"
}
}
}
$ConfigData = @{
AllNodes =
@(
@{
NodeName = "LocalHost"
}
)
}
JENKINS_CI -JenkinsPort 80 -ConfigurationData $ConfigData
Start-DscConfiguration -Path .\JENKINS_CI -Wait -Verbose
Within five to ten minutes, the Jenkins server will be configured and ready to go.