Jenkins jobs are usually created manually, but for large-scale deployments or dynamic environments, this is unsustainable. The Job DSL plugin automates Jenkins job creation, allowing you to define jobs as code. This means version control, reproducibility, and programmatic updates for your entire Jenkins job catalog.
Here’s a Jenkins instance with a few jobs already set up:
# Jenkins UI - Manage Jenkins -> Configure System
# This is where you'd set up SCMs, global tools, etc.
# For this example, we'll assume a simple setup.
# A typical Jenkins job might look like this in its XML config:
# <com.cloudbees.hudson.plugins.groovy.ProjectScmCoordinator plugin="job-dsl@1.87">
# <scm class="hudson.scm.NullSCM"/>
# </com.cloudbees.hudson.plugins.job-dsl.ProjectScmCoordinator>
# <builders>
# <hudson.tasks.Shell>
# <command>echo "Hello from a generated job!"</command>
# </hudson.tasks.Shell>
# </builders>
# ... other configurations ...
The core idea is to have a "seed job" that, when run, executes a Groovy script. This script uses the Job DSL API to define new Jenkins jobs.
Let’s set up a seed job.
-
Install the Job DSL Plugin: Go to
Manage Jenkins->Manage Plugins->Availableand search for "Job DSL". Install it. -
Create a Seed Job:
- Go to
New Item->Freestyle project. Name itjob-dsl-seed. - Under
Build, selectProcess Job DSLs. - DSL Script: Choose
Use a script from SCMorLook on Jenkins (file or text). For simplicity, let’s useLook on Jenkins (file or text). - In the
Scripttext area, paste the following Groovy script:
// job-dsl-seed.groovy def jobsToCreate = [ [name: 'hello-world-job-1', message: 'Hello from job 1!'], [name: 'hello-world-job-2', message: 'Greetings from job 2!'] ] jobsToCreate.each { jobInfo -> job(jobInfo.name) { description("A dynamically generated job for ${jobInfo.name}") scm { git { remote { url('https://github.com/jenkinsci/job-dsl-plugin.git') // Example repo branches('main') } } } steps { shell("echo \"${jobInfo.message}\"") } publishers { archiveArtifacts("*.log") } } } - Go to
-
Save and Build: Save the seed job and click
Build Now.
After the build completes, you’ll see two new jobs, hello-world-job-1 and hello-world-job-2, listed on your Jenkins dashboard. If you look at their configurations, you’ll see they’ve been created with the specified description, SCM, and shell steps.
The mental model is that the seed job is a generator. It doesn’t do the work itself; it creates other jobs that do the work. The Job DSL script is the blueprint for these generated jobs.
The real power comes when you store your DSL scripts in a Version Control System (VCS) like Git.
-
Modify the Seed Job:
- Edit your
job-dsl-seedjob. - In the
Process Job DSLssection, changeLook on JenkinstoUse a script from SCM. - SCM: Select
Git. - Repository URL: Enter the URL of a Git repository.
- Credentials: Add any necessary credentials.
- Branches to build: Specify
*/main(or your branch). - Script Path: Enter the path to your Groovy script within the repository (e.g.,
jenkins/jobs.groovy).
- Edit your
-
Create
jenkins/jobs.groovyin your Git Repo:// jenkins/jobs.groovy // This script is now in Git and will be executed by the seed job. def serviceJobs = [ [name: 'backend-service-deploy', repo: 'git@github.com:myorg/backend-service.git', branch: 'main'], [name: 'frontend-service-deploy', repo: 'git@github.com:myorg/frontend-service.git', branch: 'develop'] ] serviceJobs.each { service -> job(service.name) { description("Deploys the ${service.name} service") scm { git { remote { url(service.repo) branches(service.branch) } } } triggers { // Example: Trigger on changes to the job config itself scm() } steps { maven('clean install') { // Configure Maven settings if needed } shell('docker build -t myregistry/${JOB_NAME}:${BUILD_ID} .') shell('docker push myregistry/${JOB_NAME}:${BUILD_ID}') } // Example: Parameterized job parameters { stringParam('DEPLOY_ENV', 'staging', 'Deployment environment (staging/production)') } } } -
Commit and Build: Commit this script to your Git repository. Then, trigger a build of the
job-dsl-seedjob. Jenkins will fetch the script and createbackend-service-deployandfrontend-service-deployjobs.
The job() block is the fundamental unit. Inside it, you can configure almost any aspect of a Jenkins job. The scm {}, steps {}, triggers {}, and parameters {} blocks are just a few examples. You can also define jobs as pipelineJob, multibranchPipeline, etc., using similar DSL syntax.
The most surprising thing about Job DSL is how it blurs the lines between Jenkins configuration and application code. You’re not just configuring Jenkins; you’re programming your Jenkins instance. This means you can use standard programming constructs like loops, conditionals, and functions (or Groovy def methods) to generate complex job structures. For instance, you could write a function that takes a list of microservices and generates a build, test, and deploy job for each, all parameterized by their respective Git repositories and branches.
What people often miss is the ability to remove jobs programmatically. If a job defined by your DSL script is no longer present in the script, running the seed job will not automatically delete it. You need to explicitly manage job lifecycle. To achieve deletion, you can use the cleanupJobs() block within your seed job’s DSL script. This block tells the Job DSL plugin to find jobs that are not defined in the current DSL script and remove them. This is crucial for maintaining a clean job catalog when your application infrastructure evolves.
The next step is often managing complex pipeline configurations using the Pipeline DSL, which is a related but distinct plugin.