diff --git a/README.md b/README.md index 9bca9d2..033bfc5 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,8 @@ This repository contains utility scripts to run/configure DevOp systems in Azure * [unsecure-jenkins-instance.sh](jenkins/unsecure-jenkins-instance.sh): Disables the security of a Jenkins instance. * [Jenkins-Windows-Init-Script.ps1](powershell/Jenkins-Windows-Init-Script.ps1): Sample script on how to setup your Windows Azure Jenkins Agent to communicate through JNLP with the Jenkins master. * [Migrate-Image-From-Classic.ps1](powershell/Migrate-Image-From-Classic.ps1): Migrates an image from the classic image model to the new Azure Resource Manager model. - * [install_jenkins.sh](jenkins/install_jenkins.sh): Simple script to install Jenkins on a Linux VM. + * [install_jenkins.sh](jenkins/install_jenkins.sh): Bash script that installs Jenkins on a Linux VM and exposes it to the public through port 80 (login and cli are disabled). + * [install-plugins.sh](jenkins/install-plugins.sh): Script that installs Jenkins plugins on a given instance. * Spinnaker * [add_k8s_pipeline.sh](spinnaker/add_k8s_pipeline/): Adds a Kubernetes pipeline with three main stages: 1. Deploy to a development environment diff --git a/jenkins/README.md b/jenkins/README.md index 4d487b4..f9ec7be 100644 --- a/jenkins/README.md +++ b/jenkins/README.md @@ -28,7 +28,10 @@ For more informations see the [Jenkins documentation](https://jenkins.io/doc/boo ## Install Jenkins > [install_jenkins.sh](install_jenkins.sh) +Bash script that installs Jenkins on a Linux VM and exposes it to the public through port 80 (login and cli are disabled). -Bash script that installs Jenkins. +## Install Jenkins plugins +> [install-plugins.sh](install-plugins.sh) +Bash script that installs Jenkins plugins on a given instance. ## Questions/Comments? azdevopspub@microsoft.com \ No newline at end of file diff --git a/jenkins/install-plugins.sh b/jenkins/install-plugins.sh new file mode 100644 index 0000000..5031087 --- /dev/null +++ b/jenkins/install-plugins.sh @@ -0,0 +1,94 @@ +#!/bin/bash + +function print_usage() { + cat <&2 + print_usage + exit -1 + fi +} + +#set defaults +artifacts_location="https://raw.githubusercontent.com/Azure/azure-devops-utils/master/" + + +while [[ $# > 0 ]] +do + key="$1" + shift + case $key in + --jenkins_url|-j) + jenkins_url="$1" + shift + ;; + --jenkins_user_name|-ju) + jenkins_user_name="$1" + shift + ;; + --jenkins_password|-jp) + jenkins_password="$1" + shift + ;; + --plugins|-p) + plugins="$1" + shift + ;; + --help|-help|-h) + print_usage + exit 13 + ;; + *) + echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 + exit -1 + esac +done + +throw_if_empty --jenkins_url $jenkins_url +throw_if_empty --jenkins_user_name $jenkins_user_name +throw_if_empty --plugins $plugins + +if [ "$jenkins_user_name" != "admin" ]; then + throw_if_empty --jenkins_password $jenkins_password +fi + +function retry_until_successful { + counter=0 + "${@}" + while [ $? -ne 0 ]; do + if [[ "$counter" -gt 20 ]]; then + exit 1 + else + let counter++ + fi + sleep 5 + "${@}" + done; +} + +#download jenkins cli (wait for Jenkins to be online) +retry_until_successful wget ${jenkins_url}/jnlpJars/jenkins-cli.jar -O jenkins-cli.jar + +if [ -z "$jenkins_password" ]; then + # NOTE: Intentionally setting this after the first retry_until_successful to ensure the initialAdminPassword file exists + jenkins_password=`sudo cat /var/lib/jenkins/secrets/initialAdminPassword` +fi + +#install the required plugins +pluginsArray=(${plugins//,/ }) +for plugin_name in "${pluginsArray[@]}"; do + retry_until_successful java -jar jenkins-cli.jar -s ${jenkins_url} install-plugin "${plugin_name}" -deploy --username "${jenkins_user_name}" --password "${jenkins_password}" +done \ No newline at end of file diff --git a/jenkins/install_jenkins.sh b/jenkins/install_jenkins.sh index d7d7d32..e3eb13a 100644 --- a/jenkins/install_jenkins.sh +++ b/jenkins/install_jenkins.sh @@ -1,7 +1,162 @@ #!/bin/bash +function print_usage() { + cat <&2 + print_usage + exit -1 + fi +} + +#defaults +artifacts_location="https://raw.githubusercontent.com/Azure/azure-devops-utils/master/" +azure_web_page_location="/usr/share/nginx/azure" + +while [[ $# > 0 ]] +do + key="$1" + shift + case $key in + --jenkins_fqdn|-jf) + jenkins_fqdn="$1" + shift + ;; + --artifacts_location|-al) + artifacts_location="$1" + shift + ;; + --sas_token|-st) + artifacts_location_sas_token="$1" + shift + ;; + --help|-help|-h) + print_usage + exit 13 + ;; + *) + echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 + exit -1 + esac +done + +throw_if_empty --jenkins_fqdn $jenkins_fqdn + +jenkins_auth_matrix_conf=$(cat < + com.cloudbees.plugins.credentials.CredentialsProvider.Create:authenticated + com.cloudbees.plugins.credentials.CredentialsProvider.Delete:authenticated + com.cloudbees.plugins.credentials.CredentialsProvider.ManageDomains:authenticated + com.cloudbees.plugins.credentials.CredentialsProvider.Update:authenticated + com.cloudbees.plugins.credentials.CredentialsProvider.View:authenticated + hudson.model.Computer.Build:authenticated + hudson.model.Computer.Configure:authenticated + hudson.model.Computer.Connect:authenticated + hudson.model.Computer.Create:authenticated + hudson.model.Computer.Delete:authenticated + hudson.model.Computer.Disconnect:authenticated + hudson.model.Hudson.Administer:authenticated + hudson.model.Hudson.ConfigureUpdateCenter:authenticated + hudson.model.Hudson.Read:authenticated + hudson.model.Hudson.RunScripts:authenticated + hudson.model.Hudson.UploadPlugins:authenticated + hudson.model.Item.Build:authenticated + hudson.model.Item.Cancel:authenticated + hudson.model.Item.Configure:authenticated + hudson.model.Item.Create:authenticated + hudson.model.Item.Delete:authenticated + hudson.model.Item.Discover:authenticated + hudson.model.Item.Move:authenticated + hudson.model.Item.Read:authenticated + hudson.model.Item.Workspace:authenticated + hudson.model.Run.Delete:authenticated + hudson.model.Run.Replay:authenticated + hudson.model.Run.Update:authenticated + hudson.model.View.Configure:authenticated + hudson.model.View.Create:authenticated + hudson.model.View.Delete:authenticated + hudson.model.View.Read:authenticated + hudson.scm.SCM.Tag:authenticated + hudson.model.Hudson.Read:anonymous + hudson.model.Item.Discover:anonymous + hudson.model.Item.Read:anonymous + +EOF +) + +nginx_reverse_proxy_conf=$(cat < /etc/apt/sources.list.d/jenkins.list' sudo apt-get update --yes sudo apt-get install jenkins --yes -sudo apt-get install jenkins --yes # sometime the first apt-get install jenkins command fails, so we try it twice \ No newline at end of file +sudo apt-get install jenkins --yes # sometime the first apt-get install jenkins command fails, so we try it twice + +#We need to install workflow-aggregator so all the options in the auth matrix are valid +curl --silent "${artifacts_location}/jenkins/install-plugins.sh${artifacts_location_sas_token}" | sudo bash -s -- -j "http://localhost:8080/" -ju "admin" -p "azure-vm-agents,windows-azure-storage,matrix-auth,workflow-aggregator" + +#allow anonymous read access +inter_jenkins_config=$(sed -zr -e"s||{auth-strategy-token}|" /var/lib/jenkins/config.xml) +final_jenkins_config=${inter_jenkins_config//'{auth-strategy-token}'/${jenkins_auth_matrix_conf}} +echo "${final_jenkins_config}" | sudo tee /var/lib/jenkins/config.xml > /dev/null + +#restart jenkins +sudo service jenkins restart + +#install nginx +sudo apt-get install nginx --yes + +#configure nginx +echo "${nginx_reverse_proxy_conf}" | sudo tee /etc/nginx/sites-enabled/default > /dev/null + +#don't show version in headers +sudo sed -i "s|.*server_tokens.*|server_tokens off;|" /etc/nginx/nginx.conf + +#install jenkins-on-azure web page +curl --silent "${artifacts_location}/jenkins/jenkins-on-azure/install-web-page.sh${artifacts_location_sas_token}" | sudo bash -s -- -u "${jenkins_fqdn}" -l "${azure_web_page_location}" -al "${artifacts_location}" -st "${artifacts_location_sas_token}" + +#restart nginx +sudo service nginx restart diff --git a/jenkins/jenkins-on-azure/azure.svg b/jenkins/jenkins-on-azure/azure.svg new file mode 100644 index 0000000..f055f92 --- /dev/null +++ b/jenkins/jenkins-on-azure/azure.svg @@ -0,0 +1,391 @@ + + + + + + + + + hostingstart-v2-02 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jenkins/jenkins-on-azure/copy.png b/jenkins/jenkins-on-azure/copy.png new file mode 100644 index 0000000..291dc77 Binary files /dev/null and b/jenkins/jenkins-on-azure/copy.png differ diff --git a/jenkins/jenkins-on-azure/headshot.png b/jenkins/jenkins-on-azure/headshot.png new file mode 100644 index 0000000..fbdb5b9 Binary files /dev/null and b/jenkins/jenkins-on-azure/headshot.png differ diff --git a/jenkins/jenkins-on-azure/index.html b/jenkins/jenkins-on-azure/index.html new file mode 100644 index 0000000..2b8987b --- /dev/null +++ b/jenkins/jenkins-on-azure/index.html @@ -0,0 +1,51 @@ + + + + + + + Jenkins On Azure + + + + + + +
+
+ +
+
+

Jenkins On Azure

+
+

This Jenkins instance does not support https, so logging in through a public IP address has been disabled (it would + expose your password and other information to eavesdropping). To securely login, you need to connect to the Jenkins + instance using SSH port forwarding.

+

ssh -L 127.0.0.1:8080:localhost:8080 username@{domain-name}

+

If you don't want to publicly expose this Jenkins instance, you need to remove the nginx reverse-proxy.

+

sudo apt-get purge nginx nginx-common

+
+
+ +
+ + + + + \ No newline at end of file diff --git a/jenkins/jenkins-on-azure/install-web-page.sh b/jenkins/jenkins-on-azure/install-web-page.sh new file mode 100644 index 0000000..9420a23 --- /dev/null +++ b/jenkins/jenkins-on-azure/install-web-page.sh @@ -0,0 +1,73 @@ +#!/bin/bash +function print_usage() { + cat <&2 + print_usage + exit -1 + fi +} + +#defaults +artifacts_location="https://raw.githubusercontent.com/Azure/azure-devops-utils/master/" + +while [[ $# > 0 ]] +do + key="$1" + shift + case $key in + --location|-l) + location="$1" + shift + ;; + --url|-u) + url="$1" + shift + ;; + --artifacts_location|-al) + artifacts_location="$1" + shift + ;; + --sas_token|-st) + artifacts_location_sas_token="$1" + shift + ;; + --help|-help|-h) + print_usage + exit 13 + ;; + *) + echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 + exit -1 + esac +done + +throw_if_empty --location $location +throw_if_empty --url $url + +#install jenkins-on-azure web page +sudo mkdir ${location} +artifacts=("headshot.png" "title.png" "azure.svg" "copy.png" "site.css" "site.js" "index.html") +for i in "${artifacts[@]}"; do + if [[ $i =~ .*html.* ]] + then + raw_resource=$(curl --silent "${artifacts_location}/jenkins/jenkins-on-azure/$i${artifacts_location_sas_token}") + final_resource=${raw_resource//'{domain-name}'/${url}} + echo "${final_resource}" | sudo tee ${location}/$i > /dev/null + else + curl --silent "${artifacts_location}/jenkins/jenkins-on-azure/$i${artifacts_location_sas_token}" -o ${location}/$i + fi +done diff --git a/jenkins/jenkins-on-azure/site.css b/jenkins/jenkins-on-azure/site.css new file mode 100644 index 0000000..ed113ee --- /dev/null +++ b/jenkins/jenkins-on-azure/site.css @@ -0,0 +1,147 @@ +html { + position: relative; + min-height: 100%; +} +body { + margin: 0; + padding: 0 0 40px 0; +} +img { + vertical-align: middle; + border: 0; +} +#page-head { + box-sizing: border-box; +} +#header { + background-color: #000000; + height: 40px; +} +#header .logo { + margin-left: 16px; +} +#jenkins-home-link { + position: absolute; + height: 40px; +} +#jenkins-head-icon { + position: absolute; + bottom: 0px; +} +#jenkins-name-icon { + position: absolute; + bottom: 3px; + left: 32px; +} +#breadcrumbBar, #footer-container, .top-sticker-inner { + background-color: #f6faf2; +} +.top-sticker, #top-sticker { + width: 100%; + z-index: 999; + position: absolute; top: 40px; left: 0px; +} +#breadcrumbs { + list-style-type: none; + padding: 0 0 0 8px; + margin: 0; + height: 2em; +} +#breadcrumbs li { + font-family: Helvetica, Arial, sans-serif; + font-size: 14px; + float: left; + line-height: 2em; + height: 2em; + color: #555753; +} +#breadcrumbs li a:link, li a:visited { + text-decoration: none; + color: #555753; +} +#breadcrumbs a.inside { + padding-right: 16px; +} +#breadcrumbs li a { + display: block; + padding-left: 0.75em; + line-height: 2em; +} + + + +#feature { + margin: 0 auto 0 auto; + overflow: hidden; + position: absolute; + width: 100%; +} +#image { + position: fixed; + top: 0; + left: 0; + z-index: -1; + width: 100%; + background-color: #0078d7; + height: 100vh; + overflow: hidden; +} +#image img { + opacity: .23; + height: 100%; + /*max-height: 90vh;*/ + display: block; + margin: auto; +} +#content { + display: block; + margin: auto; + font-family: "Segoe UI"; + font-weight: normal; + font-size: 4vmin; + color: #fff; + text-align: center; + max-width: 90vw; +} +#title h1 { + font-family: "Segoe UI Light"; + color: #fff; + font-weight: normal; + font-size: 5.5vmin; + margin: 4vmin; +} +#content_body { + text-align: left; + font-size: 3vmin; +} +#content_body p { + padding-bottom: 0.3vmin; + margin: 0.5vmin; +} +.snippet { + margin-left: 6vmin; + padding: 0.3vmin; + background-color: #f6f8fa; + font: 2vmin "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: #000; + width: 100%; +} +#clipboard { + height: 2vmin; + margin-left: 1vmin; + margin-right: 0.5vmin; + cursor: pointer; +} +footer { + padding: 11px 0; + background-color: #f6faf2; + border-top: 1px solid #d3d7cf; + border-bottom: 1px solid #f6faf2; + width: 100%; + position: absolute; + bottom: 0; + left: 0; + clear: both; + font-size: 12px; + text-align: right; +} \ No newline at end of file diff --git a/jenkins/jenkins-on-azure/site.js b/jenkins/jenkins-on-azure/site.js new file mode 100644 index 0000000..cccd744 --- /dev/null +++ b/jenkins/jenkins-on-azure/site.js @@ -0,0 +1,32 @@ +$(function() { + $('.snippet').mouseenter(function() { + $('#clipboard').remove(); + span = $(this); + temp = $(this).text(); + snippetLen = $(this).text().length; + $(this).find('img').show(); + $(this).append( + 'Copy to Clipboard'); + + $(this).parent().mouseleave(function() { + $('#clipboard').remove(); + }); + + $('#clipboard').click(function() { + var doc = document, element = span[0], range, selection; + if (doc.body.createTextRange) { + range = document.body.createTextRange(); + range.moveToElementText(element); + range.select(); + } else if (window.getSelection) { + selection = window.getSelection(); + range = document.createRange(); + range.setStart(element, 0) + range.setEndBefore($(this)[0]); + selection.removeAllRanges(); + selection.addRange(range); + } + document.execCommand('copy'); + }); + }); +}); \ No newline at end of file diff --git a/jenkins/jenkins-on-azure/title.png b/jenkins/jenkins-on-azure/title.png new file mode 100644 index 0000000..db3623b Binary files /dev/null and b/jenkins/jenkins-on-azure/title.png differ diff --git a/quickstart_template/101-jenkins.sh b/quickstart_template/101-jenkins.sh new file mode 100644 index 0000000..2d4fe85 --- /dev/null +++ b/quickstart_template/101-jenkins.sh @@ -0,0 +1,55 @@ +#!/bin/bash +function print_usage() { + cat <&2 + print_usage + exit -1 + fi +} + +artifacts_location="https://raw.githubusercontent.com/Azure/azure-devops-utils/jenkins/" + +while [[ $# > 0 ]] +do + key="$1" + shift + case $key in + --jenkins_fqdn|-jf) + jenkins_fqdn="$1" + shift + ;; + --artifacts_location|-al) + artifacts_location="$1" + shift + ;; + --sas_token|-st) + artifacts_location_sas_token="$1" + shift + ;; + --help|-help|-h) + print_usage + exit 13 + ;; + *) + echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 + exit -1 + esac +done + +throw_if_empty --jenkins_fqdn $jenkins_fqdn + +curl --silent "${artifacts_location}/jenkins/install_jenkins.sh${artifacts_location_sas_token}" | sudo bash -s -- -jf "${jenkins_fqdn}" -al "${artifacts_location}" -st "${artifacts_location_sas_token}" diff --git a/quickstart_template/201-jenkins-acr.sh b/quickstart_template/201-jenkins-acr.sh index d7b5b3b..b37afe3 100644 --- a/quickstart_template/201-jenkins-acr.sh +++ b/quickstart_template/201-jenkins-acr.sh @@ -11,6 +11,7 @@ Arguments --registry_user_name|-ru [Required] : Registry user name --registry_password|-rp [Required] : Registry password --repository|-rr [Required] : Repository targeted by the pipeline + --jenkins_fqdn|-jf [Required] : Jenkins FQDN --artifacts_location|-al : Url used to reference other scripts/artifacts. --sas_token|-st : A sas token needed if the artifacts location is private. EOF @@ -57,6 +58,10 @@ do repository="$1" shift ;; + --jenkins_fqdn|-jf) + jenkins_fqdn="$1" + shift + ;; --artifacts_location|-al) artifacts_location="$1" shift @@ -80,13 +85,14 @@ throw_if_empty --git_url $git_url throw_if_empty --registry $registry throw_if_empty --registry_user_name $registry_user_name throw_if_empty --registry_password $registry_password +throw_if_empty --jenkins_fqdn $jenkins_fqdn if [ -z "$repository" ]; then repository="${vm_user_name}/myfirstapp" fi #install jenkins -curl --silent "${artifacts_location}jenkins/install_jenkins.sh${artifacts_location_sas_token}" | sudo bash -s +curl --silent "${artifacts_location}/jenkins/install_jenkins.sh${artifacts_location_sas_token}" | sudo bash -s -- -jf "${jenkins_fqdn}" -al "${artifacts_location}" -st "${artifacts_location_sas_token}" #install git sudo apt-get install git --yes diff --git a/quickstart_template/301-jenkins-acr-spinnaker-k8s.sh b/quickstart_template/301-jenkins-acr-spinnaker-k8s.sh index 1a0cfeb..ae02088 100644 --- a/quickstart_template/301-jenkins-acr-spinnaker-k8s.sh +++ b/quickstart_template/301-jenkins-acr-spinnaker-k8s.sh @@ -18,6 +18,7 @@ Arguments --storage_account_name|-san [Required] : Storage Account name used for Spinnaker's persistent storage --storage_account_key|-sak [Required] : Storage Account key used for Spinnaker's persistent storage --azure_container_registry|-acr [Required] : Azure Container Registry url + --jenkins_fqdn|-jf [Required] : Jenkins FQDN --docker_repository|-dr : Name of the docker repository to be created in your ACR --pipeline_port|-pp : Port to target in your pipeline --artifacts_location|-al : Url used to reference other scripts/artifacts. @@ -93,6 +94,10 @@ do azure_container_registry="$1" shift ;; + --jenkins_fqdn|-jf) + jenkins_fqdn="$1" + shift + ;; --docker_repository|-dr) docker_repository="$1" shift @@ -133,6 +138,7 @@ throw_if_empty --storage_account_key $storage_account_key throw_if_empty --azure_container_registry $azure_container_registry throw_if_empty --docker_repository $docker_repository throw_if_empty --pipeline_port $pipeline_port +throw_if_empty --jenkins_fqdn $jenkins_fqdn include_kubernetes_pipeline="1" pipeline_registry="$azure_container_registry" @@ -142,4 +148,4 @@ front50_port="8081" curl --silent "${artifacts_location}quickstart_template/201-spinnaker-acr-k8s.sh${artifacts_location_sas_token}" | sudo bash -s -- -ai "$app_id" -ak "$app_key" -si "$subscription_id" -ti "$tenant_id" -un "$user_name" -rg "$resource_group" -mf "$master_fqdn" -mc "$master_count" -san "$storage_account_name" -sak "$storage_account_key" -acr "$azure_container_registry" -ikp "$include_kubernetes_pipeline" -prg "$pipeline_registry" -prp "$docker_repository" -pp "$pipeline_port" -fp "$front50_port" -al "$artifacts_location" -st "$artifacts_location_sas_token" # Configure Jenkins -curl --silent "${artifacts_location}quickstart_template/201-jenkins-acr.sh${artifacts_location_sas_token}" | sudo bash -s -- -u "$user_name" -g "$git_repository" -r "https://$azure_container_registry" -ru "$app_id" -rp "$app_key" -rr "$docker_repository" -al "$artifacts_location" -st "$artifacts_location_sas_token" +curl --silent "${artifacts_location}quickstart_template/201-jenkins-acr.sh${artifacts_location_sas_token}" | sudo bash -s -- -u "$user_name" -g "$git_repository" -r "https://$azure_container_registry" -ru "$app_id" -rp "$app_key" -rr "$docker_repository" -f "$jenkins_fqdn" -al "$artifacts_location" -st "$artifacts_location_sas_token"