├── LICENSE
├── README.md
├── defaults
└── main.yml
├── files
├── javaposse.jobdsl.plugin.GlobalJobDslSecurityConfiguration.xml
├── jenkins.CLI.xml
├── jenkins.model.DownloadSettings.xml
└── jenkins.security.QueueItemAuthenticatorConfiguration.xml
├── handlers
└── main.yml
├── tasks
├── main.yml
└── seed-job.yml
└── templates
├── admin-config.xml.j2
├── config.xml.j2
├── credentials.xml.j2
├── jenkins.model.JenkinsLocationConfiguration.xml.j2
├── jobs.groovy.j2
├── scriptApproval.xml.j2
└── seed-config.xml.j2
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Joel Wilsson
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ansible role: Jenkins with Pipeline
2 |
3 | Installs a minimal installation of Jenkins with the Pipeline plugin to create jobs.
4 | Assumes there will be one job for each git repository, and that the pipelines
5 | are specified in `Jenkinsfile` at the root of each repository.
6 |
7 | The `jenkins` user must be able to clone from the git host, so the role also
8 | install SSH keys.
9 |
10 | ```yaml
11 | - name: jenkins-pipeline
12 | jenkins_admin_password: use-a-vault-variable-for-this
13 | jenkins_ssh_private_key: jenkins-id_rsa
14 | jenkins_ssh_public_key: jenkins-id_rsa.pub
15 | jenkins_git_user: git
16 | jenkins_git_host: git.example.com
17 | jenkins_git_path: git
18 | jenkins_git_repositories:
19 | - your-repo
20 | ```
21 |
22 | See [this blog post](https://wjoel.com/posts/ansible-jenkins-pipeline-part-1.html)
23 | for more information.
24 |
25 | ## SSH keys
26 |
27 | You may need to add SSH keys to `known_hosts` for Jenkins to be able
28 | to check out git repositories. This can also be done with Ansible.
29 | Here's an example:
30 |
31 | ```yaml
32 | tasks:
33 | - name: Add git.example.com to known hosts
34 | become: yes
35 | known_hosts:
36 | path: '~jenkins/.ssh/known_hosts'
37 | name: git.example.com
38 | key: "git.example.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDKpPLDi490gas7v6DWscdzSoWOOZEAa70riTVVQlD/FXwjMA8ZX4q/InySgsEIseqvXqKwpQ+AncSnTNHIKy4Q6kT1n0tFmqyJTbBsfx26ljqZ0KkEm1PtHaNtXYpEfq1LHK8a813/HDiBpdJtbU7R5pCqFE+vd52gZyUG0WRA9CcZSmWj1PdiwfLIMx8CI0arsYbh02/pPeQ8NIGNgZ7H7cDtjqFp0bOYkO8KVJZajpr7YsYI6CBnSuAwaCicdYvAbY0EgGkHyNRFJWDLOrHyHsUOntxm7a6jFTkIxZ3Sri0R9EcpJV9/WHhW4GRm5SfqW7uHzU9qJ2/8a2s+AjP13Hv5H08iXefKQ8BzuB9qMxbC2uRq3WHAU6/T5H4/N7BTsqTE/cTEfZspUG9JeRBQMS3GQt6TCEc7EbZ1NzZjSBduwbJFop80TufED7sVT3LhkbzRDa45a45n1Q8N8vsnIrEWdCAaKwcdP1lIfzwWKoNzwznEJLNZdQr1lKa2R/nrUYLY0JUjwJyMgMYgxQriMccUqYzWBeE1McQkXFqec3Xda1hyMgA7lIe8sF5iMxZmed91GeddcUh6D0WJkkO4iI58OESlAJ28X9sVsWV/3W2kbmjeFnf/QZn6Sgc0QWFrZT4vMEMjJtWvL9itmIrON2fvAPKJIhhG6deNRfuPjw=="
39 | - name: Add git.example.com (ECDSA) to known hosts
40 | become: yes
41 | known_hosts:
42 | path: '~jenkins/.ssh/known_hosts'
43 | name: git.example.com
44 | key: "git.example.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGKIHhtR206LKUvevpSuL5nOt9LBzNVXkqRnBdBqhaXbuStPM2OXQQRUxgA3PBb05lhtbMXol7di1Qp75BDdJM4="
45 | - name: Set permissions for known_hosts
46 | become: yes
47 | file: path="~jenkins/.ssh/known_hosts" owner=jenkins group=jenkins mode=0600
48 | ```
49 |
--------------------------------------------------------------------------------
/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | jenkins_java_args: -Djava.awt.headless=true -Djenkins.install.runSetupWizard=false
3 | jenkins_java_extra_args: ""
4 | jenkins_defaults_file: /etc/default/jenkins
5 | jenkins_plugins:
6 | - git
7 | - job-dsl
8 | - workflow-aggregator
9 | - workflow-cps
10 | jenkins_seed_name: pipeline-seed
11 | jenkins_seed_template: seed-config.xml.j2
12 | jenkins_job_template: jobs.groovy.j2
13 | jenkins_version: "2.107.1"
14 | jenkins_approved_signatures:
15 | - method hudson.plugins.git.GitSCM getBranches
16 | - method hudson.plugins.git.GitSCM getUserRemoteConfigs
17 | - method hudson.plugins.git.GitSCMBackwardCompatibility getExtensions
18 | jenkins_acl_approved_signatures: []
19 | jenkins_slave_agent_port: 0
20 | jenkins_cloud_configs: ""
21 | jenkins_admin_address: nobody@example.com
22 | #jenkins_url:
23 |
--------------------------------------------------------------------------------
/files/javaposse.jobdsl.plugin.GlobalJobDslSecurityConfiguration.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 |
6 |
--------------------------------------------------------------------------------
/files/jenkins.CLI.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 |
5 |
--------------------------------------------------------------------------------
/files/jenkins.model.DownloadSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 |
5 |
--------------------------------------------------------------------------------
/files/jenkins.security.QueueItemAuthenticatorConfiguration.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Restart Jenkins
3 | service: name=jenkins state=restarted
4 |
--------------------------------------------------------------------------------
/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Install ca-certificates
3 | apt: name=ca-certificates state=present
4 |
5 | - name: Add Jenkins repository key
6 | apt_key: id=D50582E6 url=https://pkg.jenkins.io/debian/jenkins.io.key
7 |
8 | - name: Add Jenkins repository
9 | apt_repository: repo='deb http://pkg.jenkins.io/debian-stable binary/' state=present update_cache=yes
10 |
11 | - name: Install Jenkins
12 | apt: name="jenkins={{ jenkins_version }}" state=present update_cache=yes
13 |
14 | - name: Set Jenkins JAVA_ARGS
15 | lineinfile:
16 | dest: "{{ jenkins_defaults_file }}"
17 | insertbefore: "^JENKINS_ARGS.*"
18 | line: "JAVA_ARGS=\"{{ jenkins_java_args }} {{ jenkins_java_extra_args }}\""
19 | register: jenkins_defaults
20 |
21 | - name: Allow logins with Jenkins SSH key
22 | authorized_key: user=jenkins key="{{ lookup('file', jenkins_ssh_public_key) }}"
23 | when: "jenkins_ssh_public_key is defined and jenkins_ssh_public_key is not none"
24 |
25 | - name: Ensure Jenkins SSH directory exists
26 | file: path="~jenkins/.ssh" owner=jenkins group=jenkins mode=0755 state=directory
27 |
28 | - name: Install Jenkins SSH key
29 | copy: src="{{ jenkins_ssh_private_key }}" dest="~jenkins/.ssh/id_rsa" owner=jenkins group=jenkins mode=0600
30 | when: "jenkins_ssh_private_key is defined and jenkins_ssh_private_key is not none"
31 |
32 | - name: Remove initial Jenkins password
33 | file: name=/var/lib/jenkins/secrets/initialAdminPassword state=absent
34 |
35 | - name: Create Jenkins admin password hash
36 | shell: echo -n "{{ jenkins_admin_password }}{ansible_jenkins}" | sha256sum - | awk '{ print $1; }'
37 | register: jenkins_password_hash
38 |
39 | - name: Create admin user directory
40 | file: path="~jenkins/users/admin" owner=jenkins group=jenkins mode=0755 state=directory recurse=yes
41 |
42 | - name: Create admin
43 | template: src=admin-config.xml.j2 dest="~jenkins/users/admin/config.xml" force=no
44 | register: jenkins_admin_config
45 |
46 | - name: Create config
47 | template: src=config.xml.j2 dest="~jenkins/config.xml"
48 | register: jenkins_config_change
49 |
50 | - name: Create scriptApproval
51 | template: src=scriptApproval.xml.j2 dest="~jenkins/scriptApproval.xml"
52 | register: jenkins_config_change
53 |
54 | - name: Create Jenkins location configuration
55 | template:
56 | src: jenkins.model.JenkinsLocationConfiguration.xml.j2
57 | dest: "~jenkins/jenkins.model.JenkinsLocationConfiguration.xml"
58 | when: jenkins_url is defined and jenkins_url != ""
59 | register: jenkins_config_change
60 |
61 | - name: Create CLI config (disable CLI)
62 | copy: src=jenkins.CLI.xml dest="~jenkins/jenkins.CLI.xml"
63 |
64 | - name: Create queue item authenticator configuration
65 | copy:
66 | src: jenkins.security.QueueItemAuthenticatorConfiguration.xml
67 | dest: "~jenkins/jenkins.security.QueueItemAuthenticatorConfiguration.xml"
68 |
69 | - name: Create job-dsl security configuration
70 | copy:
71 | src: javaposse.jobdsl.plugin.GlobalJobDslSecurityConfiguration.xml
72 | dest: "~jenkins/javaposse.jobdsl.plugin.GlobalJobDslSecurityConfiguration.xml"
73 |
74 | - name: Create credentials configuration
75 | template:
76 | src: credentials.xml.j2
77 | dest: "~jenkins/credentials.xml"
78 |
79 | - name: Create download settings configuration
80 | copy:
81 | src: jenkins.model.DownloadSettings.xml
82 | dest: "~jenkins/jenkins.model.DownloadSettings.xml"
83 |
84 | - name: Create /var/lib/jenkins/secrets
85 | file: path="/var/lib/jenkins/secrets" owner=jenkins group=jenkins mode=0700 state=directory recurse=yes
86 |
87 | - name: Enable master to slave access control
88 | copy:
89 | content: "false"
90 | dest: "/var/lib/jenkins/secrets/slave-to-master-security-kill-switch"
91 |
92 | # Restart with a task instead of a handler, since we need those changes to
93 | # be applied right away so that we can use the admin password in API calls.
94 | - name: Restart Jenkins if necessary
95 | service: name=jenkins state=restarted
96 | when: jenkins_defaults|changed or jenkins_admin_config|changed or jenkins_config|changed
97 |
98 | - name: Wait for Jenkins to become available
99 | wait_for: port=8080
100 |
101 | - name: Get Jenkins crumb
102 | uri:
103 | user: admin
104 | password: "{{ jenkins_admin_password }}"
105 | force_basic_auth: yes
106 | url: "http://127.0.0.1:8080/crumbIssuer/api/json"
107 | return_content: yes
108 | status_code: 200, 404
109 | register: jenkins_crumb
110 | until: jenkins_crumb.status == 200 and jenkins_crumb.content.find('Please wait while Jenkins is getting ready to work') == -1
111 | retries: 10
112 | delay: 5
113 |
114 | - name: Set crumb token
115 | set_fact:
116 | jenkins_crumb_token: "{{ jenkins_crumb.json.crumbRequestField }}={{ jenkins_crumb.json.crumb }}"
117 |
118 | - name: Get installed plugins
119 | uri:
120 | user: admin
121 | password: "{{ jenkins_admin_password }}"
122 | force_basic_auth: yes
123 | url: "http://127.0.0.1:8080/pluginManager/api/json?tree=plugins[shortName]&{{ jenkins_crumb_token }}"
124 | return_content: yes
125 | register: jenkins_installed_plugins
126 |
127 | - name: Install plugins
128 | uri:
129 | user: admin
130 | password: "{{ jenkins_admin_password }}"
131 | force_basic_auth: yes
132 | url: "http://127.0.0.1:8080/pluginManager/install?plugin.{{ item }}.default=on&{{ jenkins_crumb_token }}"
133 | method: POST
134 | status_code: [200, 302]
135 | when: item not in jenkins_installed_plugins.json.plugins|map(attribute='shortName')|list
136 | with_items: "{{ jenkins_plugins }}"
137 |
138 | - name: Wait for plugins to be installed
139 | uri:
140 | user: admin
141 | password: "{{ jenkins_admin_password }}"
142 | force_basic_auth: yes
143 | url: "http://127.0.0.1:8080/updateCenter/installStatus?{{ jenkins_crumb_token }}"
144 | return_content: yes
145 | register: jenkins_plugin_status
146 | until: "'Pending' not in jenkins_plugin_status.json.data.jobs|map(attribute='installStatus')"
147 | retries: 60
148 | delay: 10
149 |
150 | - name: Add cloud configs to config.xml
151 | replace:
152 | dest: "~jenkins/config.xml"
153 | regexp: ""
154 | replace: |
155 |
156 | {{ jenkins_cloud_configs }}
157 |
158 |
159 | - name: Check if we need to restart Jenkins to activate plugins
160 | uri:
161 | user: admin
162 | password: "{{ jenkins_admin_password }}"
163 | force_basic_auth: yes
164 | url: "http://127.0.0.1:8080/updateCenter/api/json\
165 | ?tree=restartRequiredForCompletion&{{ jenkins_crumb_token }}"
166 | return_content: yes
167 | register: jenkins_restart_required
168 |
169 | - name: Restart Jenkins to activate new plugins
170 | service: name=jenkins state=restarted
171 | when: jenkins_restart_required.json.restartRequiredForCompletion|bool
172 |
173 | - name: Wait for Jenkins to become available
174 | wait_for: port=8080
175 |
176 | - include_tasks: seed-job.yml
177 |
--------------------------------------------------------------------------------
/tasks/seed-job.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Get list of jobs
3 | uri:
4 | user: admin
5 | password: "{{ jenkins_admin_password }}"
6 | force_basic_auth: yes
7 | url: "http://127.0.0.1:8080/api/json?tree=jobs[name]"
8 | return_content: yes
9 | register: jenkins_jobs
10 | until: jenkins_jobs.content.find('Please wait while Jenkins is getting ready to work') == -1
11 | retries: 10
12 | delay: 5
13 |
14 | - name: Check if seed job exists
15 | set_fact:
16 | jenkins_seed_job_exists: "{{ jenkins_seed_name in (jenkins_jobs.content|from_json).jobs|map(attribute='name')|list }}"
17 |
18 | - name: Create seed job
19 | uri:
20 | user: admin
21 | password: "{{ jenkins_admin_password }}"
22 | force_basic_auth: yes
23 | url: "http://127.0.0.1:8080/createItem?name={{ jenkins_seed_name }}&{{ jenkins_crumb_token }}"
24 | method: POST
25 | headers:
26 | Content-Type: "application/xml"
27 | body: "{{ lookup('template', jenkins_seed_template) }}"
28 | register: jenkins_seed_job_created
29 | when: not jenkins_seed_job_exists
30 |
31 | - name: Update seed job
32 | uri:
33 | user: admin
34 | password: "{{ jenkins_admin_password }}"
35 | force_basic_auth: yes
36 | url: "http://127.0.0.1:8080/job/{{ jenkins_seed_name }}/config.xml?{{ jenkins_crumb_token }}"
37 | method: POST
38 | headers:
39 | Content-Type: "application/xml"
40 | body: "{{ lookup('template', jenkins_seed_template) }}"
41 | register: jenkins_seed_job_updated
42 | when: jenkins_seed_job_exists
43 |
44 | - name: Run seed job
45 | uri:
46 | user: admin
47 | password: "{{ jenkins_admin_password }}"
48 | force_basic_auth: yes
49 | url: "http://127.0.0.1:8080/job/{{ jenkins_seed_name }}/build?{{ jenkins_crumb_token }}"
50 | method: POST
51 | status_code: 201
52 | register: jenkins_seed_job_started
53 | when: jenkins_seed_job_created|success or jenkins_seed_job_updated|success
54 |
55 | - name: Wait for seed job
56 | uri:
57 | user: admin
58 | password: "{{ jenkins_admin_password }}"
59 | force_basic_auth: yes
60 | url: "http://127.0.0.1:8080/job/{{ jenkins_seed_name }}/lastBuild/buildNumber?{{ jenkins_crumb_token }}"
61 | method: GET
62 | status_code: 200
63 | register: jenkins_seed
64 | until: jenkins_seed['status']|default(0) == 200
65 | retries: 10
66 | delay: 5
67 |
--------------------------------------------------------------------------------
/templates/admin-config.xml.j2:
--------------------------------------------------------------------------------
1 |
2 |
3 | admin
4 |
5 |
6 |
7 |
8 |
9 | All
10 | false
11 | false
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | false
21 |
22 |
23 | ansible_jenkins:{{ jenkins_password_hash.stdout }}
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/templates/config.xml.j2:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1.0
5 | 2
6 | NORMAL
7 | true
8 |
9 | true
10 |
11 |
12 | true
13 | false
14 |
15 | false
16 |
17 | ${ITEM_ROOTDIR}/workspace
18 | ${ITEM_ROOTDIR}/builds
19 |
20 |
21 |
22 |
23 |
24 | 0
25 |
26 |
27 |
28 | All
29 | false
30 | false
31 |
32 |
33 |
34 | All
35 | {{ jenkins_slave_agent_port }}
36 |
37 | JNLP-connect
38 | JNLP2-connect
39 | JNLP3-connect
40 |
41 |
42 |
43 | false
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/templates/credentials.xml.j2:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | GLOBAL
11 | {{ jenkins_git_user }}
12 |
13 | {{ jenkins_git_user }}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/templates/jenkins.model.JenkinsLocationConfiguration.xml.j2:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ jenkins_admin_address }}
4 | {{ jenkins_url }}
5 |
6 |
--------------------------------------------------------------------------------
/templates/jobs.groovy.j2:
--------------------------------------------------------------------------------
1 | {% for repository in jenkins_git_repositories %}
2 | pipelineJob('{{ repository }}') {
3 | triggers {
4 | scm ''
5 | }
6 | definition {
7 | cpsScm {
8 | scm {
9 | git {
10 | remote {
11 | url('{{ jenkins_git_user }}@{{ jenkins_git_host }}:{{ jenkins_git_path }}/{{ repository }}.git')
12 | credentials('{{ jenkins_git_user }}')
13 | }
14 | branch('master')
15 | }
16 | }
17 | scriptPath('Jenkinsfile')
18 | }
19 | }
20 | }
21 |
22 | {% endfor %}
23 |
--------------------------------------------------------------------------------
/templates/scriptApproval.xml.j2:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {% for signature in jenkins_approved_signatures %}
5 | {{ signature }}
6 | {% endfor %}
7 |
8 |
9 | {% for signature in jenkins_acl_approved_signatures %}
10 | {{ signature }}
11 | {% endfor %}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/templates/seed-config.xml.j2:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | false
6 |
7 |
8 | true
9 | false
10 | false
11 | false
12 |
13 | false
14 |
15 |
16 | {{ lookup('template', jenkins_job_template) }}
17 |
18 | true
19 | false
20 | false
21 | false
22 | false
23 | false
24 | DELETE
25 | DELETE
26 | IGNORE
27 | JENKINS_ROOT
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------