├── .gitignore ├── LICENSE ├── README.md ├── bash ├── README.md └── create-service-principal.sh ├── deploy-dev-ops ├── README.md └── deploy-dev-ops.sh ├── jenkins ├── README.md ├── add-aptly-build-job.sh ├── add-docker-build-job.sh ├── basic-aptly-build-job.xml ├── basic-docker-build-job.xml ├── basic-docker-build.groovy ├── basic-user-pwd-credentials.xml ├── blue-green │ ├── add-blue-green-job.sh │ ├── aks-k8s-blue-green-job.xml │ ├── bootstrap-k8s-blue-green.sh │ ├── fetch-k8s-deployment-config.sh │ ├── k8s │ │ ├── deployment.yml │ │ ├── service.yml │ │ └── test-endpoint.yml │ ├── sp-credentials.xml │ ├── ssh-credentials.xml │ └── verified-jenkins-version ├── init-aptly-repo.sh ├── install_jenkins.sh ├── jenkins-on-azure │ ├── azure.svg │ ├── copy.png │ ├── headshot.png │ ├── index.html │ ├── install-web-page.sh │ ├── site.css │ ├── site.js │ └── title.png ├── jenkins-verified-ver ├── run-cli-command.sh └── unsecure-jenkins-instance.sh ├── powershell ├── Jenkins-Windows-Init-Script-SSH.ps1 ├── Jenkins-Windows-Init-Script-no-secrets.ps1 ├── Jenkins-Windows-Init-Script.ps1 ├── Migrate-Image-From-Classic.ps1 └── README.md ├── quickstart_template ├── 101-spinnaker-aks.sh ├── 101-spinnaker-install-selection.sh ├── 101-spinnaker.sh ├── 201-jenkins-acr.sh ├── 301-jenkins-acr-spinnaker-k8s.sh ├── 301-jenkins-aptly-spinnaker-vmss.sh └── 301-jenkins-k8s-blue-green.sh ├── solution_template ├── jenkins │ ├── createUiDefinition.json │ └── mainTemplate.json ├── nested │ ├── VM-new-password.json │ ├── VM-new-sshPublicKey.json │ ├── VM-password.json │ ├── VM-sshPublicKey.json │ ├── publicIP-existing.json │ ├── publicIP-new.json │ ├── spinnaker-VM-Init.json │ ├── storageAccount-existing.json │ └── storageAccount-new.json └── spinnaker │ ├── createUiDefinition.json │ └── mainTemplate.json └── spinnaker ├── add_k8s_pipeline ├── README.md ├── add_k8s_pipeline.sh ├── application.json ├── load_balancer.json └── pipeline.json ├── copy_kube_config ├── README.md └── copy_kube_config.sh └── install_halyard ├── README.md └── install_halyard.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 azure-devops 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Azure DevOps Utilities 2 | This repository contains utility scripts to run/configure DevOp systems in Azure. These scripts can be used individually, but are also leveraged in several 'Getting Started' solutions: 3 | * [Jenkins Quickstart Templates](https://azure.microsoft.com/resources/templates/?term=Jenkins) 4 | * [Spinnaker Quickstart Templates](https://azure.microsoft.com/resources/templates/?term=Spinnaker) 5 | 6 | ## Contents 7 | * Common 8 | * [create-service-principal.sh](bash/create-service-principal.sh): Creates Azure Service Principal credentials. 9 | * [deploy-dev-ops.sh](deploy-dev-ops/): Deploys a DevOps pipeline targeting either a Kubernetes cluster or VM Scale Sets. 10 | * Jenkins 11 | * [basic-docker-build.groovy](jenkins/basic-docker-build.groovy): Sample Jenkins pipeline that clones a git repository, builds the docker container defined in the Docker file and pushes that container to a private container registry. 12 | * [add-docker-build-job.sh](jenkins/add-docker-build-job.sh): Adds a Docker Build job in an existing Jenkins instance. 13 | * [add-aptly-build-job.sh](jenkins/add-aptly-build-job.sh): Adds a sample Build job in an existing Jenkins instance that pushes a debian package to an Aptly repository. 14 | * [init-aptly-repo.sh](jenkins/init-aptly-repo.sh): Initializes an Aptly repository on an existing Jenkins instance. 15 | * [unsecure-jenkins-instance.sh](jenkins/unsecure-jenkins-instance.sh): Disables the security of a Jenkins instance. 16 | * [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. 17 | * [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. 18 | * [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). 19 | * [run-cli-command.sh](jenkins/run-cli-command.sh): Script that runs a Jenkins CLI command. 20 | * Spinnaker 21 | * [add_k8s_pipeline.sh](spinnaker/add_k8s_pipeline/): Adds a Kubernetes pipeline with three main stages: 22 | 1. Deploy to a development environment 23 | 1. Wait for manual judgement 24 | 1. Deploy to a production environment 25 | * [copy_kube_config.sh](spinnaker/copy_kube_config/): Programatically copies a kubeconfig file from an Azure Container Service Kubernetes cluster to a Spinnaker machine. 26 | * [install_halyard.sh](spinnaker/install_halyard/): Install Halyard and automatically configure Spinnaker to use Azure Storage (azs) as its persistent storage. 27 | 28 | ## Questions/Comments? azdevopspub@microsoft.com 29 | 30 | _This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments._ -------------------------------------------------------------------------------- /bash/README.md: -------------------------------------------------------------------------------- 1 | ## Create Service Principal [Deprecated] 2 | 3 | It is now recommended to use [version 2.0](https://docs.microsoft.com/cli/azure/install-azure-cli) of the Azure CLI: 4 | ```bash 5 | az login 6 | az account set --subscription 7 | az ad sp create-for-rbac 8 | ``` 9 | By default, the last command creates a Service Principal with the 'Contributor' role scoped to the current subscription. Pass the '--help' parameter for more info if you want to change the defaults. 10 | 11 | See [here](https://docs.microsoft.com/cli/azure/create-an-azure-service-principal-azure-cli?toc=%2fazure%2fazure-resource-manager%2ftoc.json) for more information 12 | 13 | If you still want to use [version 1.0](https://docs.microsoft.com/azure/cli-install-nodejs) of the Azure CLI, then use this script. It prompts for user input to be able to authenticate on Azure and to pick the desired subscription (this step can be skipped by providing the subscription id as a script argument). 14 | 15 | ## Questions/Comments? azdevopspub@microsoft.com 16 | -------------------------------------------------------------------------------- /bash/create-service-principal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat < 10 | az ad sp create-for-rbac 11 | 12 | By default, the last command creates a Service Principal with the 'Contributor' role scoped to the 13 | current subscription. Pass the '--help' parameter for more info if you want to change the defaults. 14 | ********************************************** WARNING ********************************************** 15 | EOF 16 | 17 | if !(command -v azure >/dev/null); then 18 | echo "ERROR: This script requires Azure CLI 1.0, but it could not be found. Is it installed and on your path?" 1>&2 19 | exit -1 20 | fi 21 | 22 | SUBSCRIPTION_ID=$1 23 | 24 | #echo "" 25 | #echo " Background article: https://azure.microsoft.com/documentation/articles/resource-group-authenticate-service-principal" 26 | echo "" 27 | 28 | my_app_name_uuid=$(python -c 'import uuid; print (str(uuid.uuid4())[:8])') 29 | MY_APP_NAME="app${my_app_name_uuid}" 30 | 31 | MY_APP_KEY=$(python -c 'import uuid; print (uuid.uuid4().hex)') 32 | 33 | my_app_id_URI="${MY_APP_NAME}_id" 34 | 35 | #check if the user has subscriptions. If not she's probably not logged in 36 | subscriptions_list=$(azure account list --json) 37 | subscriptions_list_count=$(echo $subscriptions_list | jq '. | length' 2>/dev/null) 38 | if [ $? -ne 0 ] || [ "$subscriptions_list_count" -eq "0" ] 39 | then 40 | azure login 41 | else 42 | echo " You are already logged in with an Azure account so we won't ask for credentials again." 43 | echo " If you want to select a subscription from a different account, before running this script you should either log out from all the Azure accounts or login manually with the new account." 44 | echo " azure login" 45 | echo "" 46 | fi 47 | 48 | if [ -z "$SUBSCRIPTION_ID" ] 49 | then 50 | #prompt for subscription 51 | subscription_index=0 52 | subscriptions_list=$(azure account list --json) 53 | subscriptions_list_count=$(echo $subscriptions_list | jq '. | length') 54 | if [ $subscriptions_list_count -eq 0 ] 55 | then 56 | echo " You need to sign up an Azure Subscription here: https://azure.microsoft.com" 57 | exit 1 58 | elif [ $subscriptions_list_count -gt 1 ] 59 | then 60 | echo $subscriptions_list | jq -r 'keys[] as $i | " \($i+1). \(.[$i] | .name)"' 61 | 62 | while read -r -t 0; do read -r; done #clear stdin 63 | subscription_idx=0 64 | until [ $subscription_idx -ge 1 -a $subscription_idx -le $subscriptions_list_count ] 65 | do 66 | read -p " Select a subscription by typing an index number from above list and press [Enter]: " subscription_idx 67 | if [ $subscription_idx -ne 0 -o $subscription_idx -eq 0 2>/dev/null ] 68 | then 69 | : 70 | else 71 | subscription_idx=0 72 | fi 73 | done 74 | subscription_index=$((subscription_idx-1)) 75 | fi 76 | 77 | SUBSCRIPTION_ID=`echo $subscriptions_list | jq -r '.['$subscription_index'] | .id'` 78 | echo "" 79 | fi 80 | 81 | azure account set $SUBSCRIPTION_ID >/dev/null 82 | if [ $? -ne 0 ] 83 | then 84 | exit 1 85 | else 86 | echo " Using subscription ID $SUBSCRIPTION_ID" 87 | echo "" 88 | fi 89 | 90 | MY_SUBSCRIPTION_ID=$(azure account show --json | jq -r '.[0].id') 91 | MY_TENANT_ID=$(azure account show --json | jq -r '.[0].tenantId') 92 | 93 | azure config mode arm >/dev/null 94 | 95 | my_error_check=$(azure ad sp show --search $MY_APP_NAME --json | grep "displayName" | grep -c \"$MY_APP_NAME\" ) 96 | 97 | if [ $my_error_check -gt 0 ]; 98 | then 99 | echo " Found an app id matching the one we are trying to create; we will reuse that instead" 100 | else 101 | echo " Creating application in active directory:" 102 | echo " azure ad app create --name '$MY_APP_NAME' --home-page 'http://$MY_APP_NAME' --identifier-uris 'http://$my_app_id_URI/' --password $MY_APP_KEY" 103 | azure ad app create --name $MY_APP_NAME --home-page http://$MY_APP_NAME --identifier-uris http://$my_app_id_URI/ --password $MY_APP_KEY >/dev/null 104 | if [ $? -ne 0 ] 105 | then 106 | exit 1 107 | fi 108 | # Give time for operation to complete 109 | echo " Waiting for operation to complete...." 110 | sleep 20 111 | my_error_check=$(azure ad app show --search $MY_APP_NAME --json | grep "displayName" | grep -c \"$MY_APP_NAME\" ) 112 | 113 | if [ $my_error_check -gt 0 ]; 114 | then 115 | my_app_object_id=$(azure ad app show --json --search $MY_APP_NAME | jq -r '.[0].objectId') 116 | MY_CLIENT_ID=$(azure ad app show --json --search $MY_APP_NAME | jq -r '.[0].appId') 117 | echo " " 118 | echo " Creating the service principal in AD" 119 | echo " azure ad sp create -a $MY_CLIENT_ID" 120 | azure ad sp create -a $MY_CLIENT_ID >/dev/null 121 | # Give time for operation to complete 122 | echo " Waiting for operation to complete...." 123 | sleep 20 124 | my_app_sp_object_id=$(azure ad sp show --search $MY_APP_NAME --json | jq -r '.[0].objectId') 125 | 126 | echo " Assign rights to service principle" 127 | echo " azure role assignment create --objectId $my_app_sp_object_id -o Owner -c /subscriptions/$MY_SUBSCRIPTION_ID" 128 | azure role assignment create --objectId $my_app_sp_object_id -o Owner -c /subscriptions/$MY_SUBSCRIPTION_ID >/dev/null 129 | if [ $? -ne 0 ] 130 | then 131 | exit 1 132 | fi 133 | else 134 | echo " " 135 | echo " We've encounter an unexpected error; please hit Ctr-C and retry from the beginning" 136 | read my_error 137 | fi 138 | fi 139 | 140 | MY_CLIENT_ID=$(azure ad sp show --search $MY_APP_NAME --json | jq -r '.[0].appId') 141 | 142 | echo " " 143 | echo " Your access credentials =============================" 144 | echo " " 145 | echo " Subscription ID:" $MY_SUBSCRIPTION_ID 146 | echo " Client ID:" $MY_CLIENT_ID 147 | echo " Client Secret:" $MY_APP_KEY 148 | echo " OAuth 2.0 Token Endpoint:" "https://login.microsoftonline.com/${MY_TENANT_ID}/oauth2/token" 149 | echo " Tenant ID:" $MY_TENANT_ID 150 | echo " " 151 | echo " You can verify the service principal was created properly by running:" 152 | echo " azure login -u "$MY_CLIENT_ID" --service-principal --tenant $MY_TENANT_ID" 153 | echo " " 154 | -------------------------------------------------------------------------------- /deploy-dev-ops/README.md: -------------------------------------------------------------------------------- 1 | # Deploy a Continuous Delivery pipeline [![Build Status](http://devops-ci.westcentralus.cloudapp.azure.com/job/qs/job/deploy-dev-ops/badge/icon)](http://devops-ci.westcentralus.cloudapp.azure.com/blue/organizations/jenkins/qs%2Fdeploy-dev-ops/activity) 2 | 3 | This script deploys a DevOps pipeline targeting either a Kubernetes cluster or VM Scale Sets. It deploys an instance of Jenkins and Spinnaker on an Ubuntu 14.04 VM in Azure. 4 | 5 | ## Arguments 6 | | Name | Description | 7 | | --- | ---| 8 | | --subscription_id
-s | Subscription id, optional if a default is already set in the Azure CLI | 9 | | --deploy_target
-dt | Deployment target for Spinnaker (either 'k8s' for a Kubernetes cluster or 'vmss' for VM Scale Sets), defaulted to 'k8s' | 10 | | --username
-u | Username for the DevOps VM, defaulted to 'azureuser' | 11 | | --dns_prefix
-dp | DNS prefix for the DevOps VM, defaulted to a generated string | 12 | | --resource_group
-rg | Resource group to deploy to, defaulted to a generated string | 13 | | --location
-l | Location to deploy to, e.g. 'westus', optional if a default is already set in the Azure CLI | 14 | | --app_id
-ai | Service Principal App Id (also called client id), defaulted to a generated Service Principal | 15 | | --app_key
-ak | Service Principal App Key (also called client secret), defaulted to a generated Service Principal | 16 | | --tenant_id
-ti | Tenant Id (only necessary if you want this script to log in to the cli with the Service Principal credentials) | 17 | | --password
-p | Password for the DevOps VM (only used for the 'vmss' scenario) | 18 | | --ssh_public_key
-spk | SSH Public Key for the DevOps VM (only used for the 'k8s' scenario), defaulted to '~/.ssh/id_rsa.pub' | 19 | | --git_repository
-gr | Git repository with a Dockerfile at the root (only used for the 'k8s' scenario), defaulted to 'https://github.com/azure-devops/spin-kub-demo' | 20 | | --quiet
-q | If this flag is passed, the script will not prompt for any values. An error will be thrown if a required parameter is not specified. | 21 | 22 | ## Example usage 23 | To run interactively: 24 | ```bash 25 | bash <(curl -sL https://aka.ms/DeployDevOps) 26 | ``` 27 | 28 | To run non-interactively: 29 | ```bash 30 | curl -sL https://aka.ms/DeployDevOps | bash -s -- 31 | ``` 32 | 33 | ## Questions/Comments? azdevopspub@microsoft.com -------------------------------------------------------------------------------- /jenkins/README.md: -------------------------------------------------------------------------------- 1 | ## Jenkins groovy script to build and push a Docker container 2 | > [basic-docker-build.groovy](basic-docker-build.groovy) 3 | 4 | Sample Jenkins pipeline that clones a git repository, builds the docker container defined in the Docker file and pushes that container to a private container registry. 5 | The Jenkins Job that uses this groovy script must have these parameters defined: 6 | 7 | | Jenkins job parameters | Description | 8 | |-------------------------|-------------------------------------------------------------------------------------------------------------| 9 | | git_repo | A public git repository that has a Dockerfile | 10 | | docker_repository | The container repository | 11 | | registry_url | The Docker private container registry url | 12 | | registry_credentials_id | The Jenkins credentials id that stores the user name and password for the Docker private container registry | 13 | 14 | ## Add a Docker Build job in an existing Jenkins instance 15 | > [add-docker-build-job.sh](add-docker-build-job.sh) 16 | 17 | Bash script that adds a Docker Build job in an existing Jenkins instance. The created job will use the [basic-docker-build.groovy](basic-docker-build.groovy) script. 18 | 19 | ## Disable security for a Jenkins instance 20 | > [unsecure-jenkins-instance.sh](unsecure-jenkins-instance.sh) 21 | 22 | Bash script that disables the security of a Jenkins instance. 23 | 24 | If you accidentally set up security realm / authorization in such a way that you may no longer able to reconfigure Jenkins you can use this script to disable security. 25 | 26 | ***Don't make your instance publicly available when running this script! Anyone can access your unsecure Jenkins instance!*** 27 | For more informations see the [Jenkins documentation](https://jenkins.io/doc/book/operating/security/#disabling-security) 28 | 29 | ## Install Jenkins 30 | > [install_jenkins.sh](install_jenkins.sh) 31 | Bash script that installs Jenkins on a Linux VM and exposes it to the public through port 80 (login and cli are disabled). 32 | 33 | ## Install Jenkins plugins 34 | > [run-cli-command.sh](run-cli-command.sh) 35 | Bash script that runs a Jenkins cli command. 36 | 37 | ## Questions/Comments? azdevopspub@microsoft.com -------------------------------------------------------------------------------- /jenkins/add-aptly-build-job.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function print_usage() { 4 | cat <&2 23 | print_usage 24 | exit -1 25 | fi 26 | } 27 | 28 | function run_util_script() { 29 | local script_path="$1" 30 | shift 31 | curl --silent "${artifacts_location}${script_path}${artifacts_location_sas_token}" | sudo bash -s -- "$@" 32 | local return_value=$? 33 | if [ $return_value -ne 0 ]; then 34 | >&2 echo "Failed while executing script '$script_path'." 35 | exit $return_value 36 | fi 37 | } 38 | 39 | #set defaults 40 | job_short_name="hello-karyon-rxnetty" 41 | job_display_name="Sample Aptly Job" 42 | job_description="A basic pipeline that builds a debian package and pushes it to an Aptly repository hosted on the Jenkins VM." 43 | repository_name="hello-karyon-rxnetty" 44 | artifacts_location="https://raw.githubusercontent.com/Azure/azure-devops-utils/master/" 45 | git_url="https://github.com/azure-devops/hello-karyon-rxnetty.git" 46 | 47 | while [[ $# > 0 ]] 48 | do 49 | key="$1" 50 | shift 51 | case $key in 52 | --git_url|-g) 53 | git_url="$1";; 54 | --repository_name|-rn) 55 | repository_name="$1";; 56 | --job_short_name|-jsn) 57 | job_short_name="$1";; 58 | --job_display_name|-jdn) 59 | job_display_name="$1";; 60 | --job_description|-jd) 61 | job_description="$1";; 62 | --artifacts_location|-al) 63 | artifacts_location="$1";; 64 | --sas_token|-st) 65 | artifacts_location_sas_token="$1";; 66 | --help|-help|-h) 67 | print_usage 68 | exit 13;; 69 | *) 70 | echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 71 | exit -1 72 | esac 73 | shift 74 | done 75 | 76 | throw_if_empty git_url $git_url 77 | throw_if_empty repository_name $repository_name 78 | 79 | #download dependencies 80 | job_xml=$(curl -s ${artifacts_location}/jenkins/basic-aptly-build-job.xml${artifacts_location_sas_token}) 81 | 82 | #prepare job.xml 83 | job_xml=${job_xml//'{insert-job-display-name}'/${job_display_name}} 84 | job_xml=${job_xml//'{insert-job-description}'/${job_description}} 85 | job_xml=${job_xml//'{insert-git-url}'/${git_url}} 86 | job_xml=${job_xml//'{insert-repository-name}'/${repository_name}} 87 | 88 | echo "${job_xml}" > job.xml 89 | 90 | echo "Installing git..." 91 | sudo DEBIAN_FRONTEND=noninteractive apt-get install git -y 92 | 93 | echo "Installing Java 8..." 94 | sudo DEBIAN_FRONTEND=noninteractive apt-get install -y openjdk-8-jdk 95 | 96 | gradle_version="3.5" 97 | echo "Installing Gradlew ${gradle_version}..." 98 | wget -nv https://services.gradle.org/distributions/gradle-${gradle_version}-bin.zip 99 | sudo unzip gradle-${gradle_version}-bin.zip -d /opt/gradle 100 | sudo ln -s /opt/gradle/gradle-${gradle_version}/bin/gradle /usr/bin/gradle 101 | 102 | run_util_script "jenkins/run-cli-command.sh" -c "install-plugin git -restart" 103 | 104 | echo "Waiting for Jenkins to be back online..." 105 | run_util_script "jenkins/run-cli-command.sh" -c "version" 106 | 107 | echo "Adding basic vmss job..." 108 | run_util_script "jenkins/run-cli-command.sh" -cif "job.xml" -c "create-job ${job_short_name}" 109 | 110 | #cleanup 111 | rm job.xml 112 | rm jenkins-cli.jar -------------------------------------------------------------------------------- /jenkins/add-docker-build-job.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function print_usage() { 4 | cat <&2 33 | print_usage 34 | exit -1 35 | fi 36 | } 37 | 38 | function run_util_script() { 39 | local script_path="$1" 40 | shift 41 | curl --silent "${artifacts_location}${script_path}${artifacts_location_sas_token}" | sudo bash -s -- "$@" 42 | local return_value=$? 43 | if [ $return_value -ne 0 ]; then 44 | >&2 echo "Failed while executing script '$script_path'." 45 | exit $return_value 46 | fi 47 | } 48 | 49 | #set defaults 50 | credentials_id="docker_credentials" 51 | credentials_desc="Docker Container Registry Credentials" 52 | job_short_name="basic-docker-build" 53 | job_display_name="Basic Docker Build" 54 | job_description="A basic pipeline that builds a Docker container. The job expects a Dockerfile at the root of the git repository" 55 | repository="${USER}/myfirstapp" 56 | scm_poll_schedule="" 57 | scm_poll_ignore_commit_hooks="0" 58 | artifacts_location="https://raw.githubusercontent.com/Azure/azure-devops-utils/master/" 59 | 60 | while [[ $# > 0 ]] 61 | do 62 | key="$1" 63 | shift 64 | case $key in 65 | --jenkins_url|-j) 66 | jenkins_url="$1" 67 | shift 68 | ;; 69 | --jenkins_username|-ju) 70 | jenkins_username="$1" 71 | shift 72 | ;; 73 | --jenkins_password|-jp) 74 | jenkins_password="$1" 75 | shift 76 | ;; 77 | --git_url|-g) 78 | git_url="$1" 79 | shift 80 | ;; 81 | --registry|-r) 82 | registry="$1" 83 | shift 84 | ;; 85 | --registry_user_name|-ru) 86 | registry_user_name="$1" 87 | shift 88 | ;; 89 | --registry_password|-rp) 90 | registry_password="$1" 91 | shift 92 | ;; 93 | --repository|-rr) 94 | repository="$1" 95 | shift 96 | ;; 97 | --credentials_id|-ci) 98 | credentials_id="$1" 99 | shift 100 | ;; 101 | --credentials_desc|-cd) 102 | credentials_desc="$1" 103 | shift 104 | ;; 105 | --job_short_name|-jsn) 106 | job_short_name="$1" 107 | shift 108 | ;; 109 | --job_display_name|-jdn) 110 | job_display_name="$1" 111 | shift 112 | ;; 113 | --job_description|-jd) 114 | job_description="$1" 115 | shift 116 | ;; 117 | --scm_poll_schedule|-sps) 118 | scm_poll_schedule="$1" 119 | shift 120 | ;; 121 | --scm_poll_ignore_commit_hooks|-spi) 122 | scm_poll_ignore_commit_hooks="$1" 123 | shift 124 | ;; 125 | --artifacts_location|-al) 126 | artifacts_location="$1" 127 | shift 128 | ;; 129 | --sas_token|-st) 130 | artifacts_location_sas_token="$1" 131 | shift 132 | ;; 133 | --help|-help|-h) 134 | print_usage 135 | exit 13 136 | ;; 137 | *) 138 | echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 139 | exit -1 140 | esac 141 | done 142 | 143 | throw_if_empty --jenkins_url $jenkins_url 144 | throw_if_empty --jenkins_username $jenkins_username 145 | if [ "$jenkins_username" != "admin" ]; then 146 | throw_if_empty --jenkins_password $jenkins_password 147 | fi 148 | throw_if_empty --git_url $git_url 149 | throw_if_empty --registry $registry 150 | throw_if_empty --registry_user_name $registry_user_name 151 | throw_if_empty --registry_password $registry_password 152 | 153 | #download dependencies 154 | job_xml=$(curl -s ${artifacts_location}/jenkins/basic-docker-build-job.xml${artifacts_location_sas_token}) 155 | credentials_xml=$(curl -s ${artifacts_location}/jenkins/basic-user-pwd-credentials.xml${artifacts_location_sas_token}) 156 | 157 | #prepare credentials.xml 158 | credentials_xml=${credentials_xml//'{insert-credentials-id}'/${credentials_id}} 159 | credentials_xml=${credentials_xml//'{insert-credentials-description}'/${credentials_desc}} 160 | credentials_xml=${credentials_xml//'{insert-user-name}'/${registry_user_name}} 161 | credentials_xml=${credentials_xml//'{insert-user-password}'/${registry_password}} 162 | 163 | #prepare job.xml 164 | job_xml=${job_xml//'{insert-job-display-name}'/${job_display_name}} 165 | job_xml=${job_xml//'{insert-job-description}'/${job_description}} 166 | job_xml=${job_xml//'{insert-git-url}'/${git_url}} 167 | job_xml=${job_xml//'{insert-registry}'/${registry}} 168 | job_xml=${job_xml//'{insert-docker-credentials}'/${credentials_id}} 169 | job_xml=${job_xml//'{insert-container-repository}'/${repository}} 170 | 171 | 172 | if [ -n "${scm_poll_schedule}" ] 173 | then 174 | scm_poll_ignore_commit_hooks_bool="false" 175 | if [[ "${scm_poll_ignore_commit_hooks}" == "1" ]] 176 | then 177 | scm_poll_ignore_commit_hooks_bool="true" 178 | fi 179 | triggers_xml_node=$(cat < 181 | 182 | ${scm_poll_schedule} 183 | ${scm_poll_ignore_commit_hooks_bool} 184 | 185 | 186 | EOF 187 | ) 188 | job_xml=${job_xml//''/${triggers_xml_node}} 189 | fi 190 | 191 | job_xml=${job_xml//'{insert-groovy-script}'/"$(curl -s ${artifacts_location}/jenkins/basic-docker-build.groovy${artifacts_location_sas_token})"} 192 | echo "${job_xml}" > job.xml 193 | 194 | #install the required plugins 195 | run_util_script "jenkins/run-cli-command.sh" -j "$jenkins_url" -ju "$jenkins_username" -jp "$jenkins_password" -c "install-plugin credentials -deploy" 196 | plugins=(docker-workflow git) 197 | for plugin in "${plugins[@]}"; do 198 | run_util_script "jenkins/run-cli-command.sh" -j "$jenkins_url" -ju "$jenkins_username" -jp "$jenkins_password" -c "install-plugin $plugin -restart" 199 | done 200 | 201 | #wait for instance to be back online 202 | run_util_script "jenkins/run-cli-command.sh" -j "$jenkins_url" -ju "$jenkins_username" -jp "$jenkins_password" -c "version" 203 | 204 | #add user/pwd 205 | echo "${credentials_xml}" > credentials.xml 206 | run_util_script "jenkins/run-cli-command.sh" -j "$jenkins_url" -ju "$jenkins_username" -jp "$jenkins_password" -c 'create-credentials-by-xml SystemCredentialsProvider::SystemContextResolver::jenkins (global)' -cif "credentials.xml" 207 | 208 | #add job 209 | run_util_script "jenkins/run-cli-command.sh" -j "$jenkins_url" -ju "$jenkins_username" -jp "$jenkins_password" -c "create-job ${job_short_name}" -cif "job.xml" 210 | 211 | #cleanup 212 | rm credentials.xml 213 | rm job.xml 214 | rm jenkins-cli.jar -------------------------------------------------------------------------------- /jenkins/basic-aptly-build-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {insert-job-description} 5 | {insert-job-display-name} 6 | false 7 | 8 | 9 | 28 | true 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /jenkins/basic-docker-build-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {insert-job-description} 5 | {insert-job-display-name} 6 | false 7 | 8 | 9 | 10 | 11 | git_repo 12 | Git repository from where we're going to checkout the code (master branch) and build a docker image. 13 | NB! The repository must contain a Dockerfile in the root 14 | {insert-git-url} 15 | 16 | 17 | docker_repository 18 | The docker repository 19 | {insert-container-repository} 20 | 21 | 22 | registry_url 23 | Container Registry URL 24 | {insert-registry} 25 | 26 | 27 | registry_credentials_id 28 | The credentials id that points to the Container Registry credentials 29 | {insert-docker-credentials} 30 | com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 43 | true 44 | 45 | 46 | -------------------------------------------------------------------------------- /jenkins/basic-docker-build.groovy: -------------------------------------------------------------------------------- 1 | node { 2 | def built_img = '' 3 | stage('Checkout git repo') { 4 | git branch: 'master', url: params.git_repo 5 | } 6 | stage('Build Docker image') { 7 | built_img = docker.build(params.docker_repository + ":${env.BUILD_NUMBER}", '.') 8 | } 9 | stage('Push Docker image to Azure Container Registry') { 10 | docker.withRegistry(params.registry_url, params.registry_credentials_id ) { 11 | built_img.push("${env.BUILD_NUMBER}"); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /jenkins/basic-user-pwd-credentials.xml: -------------------------------------------------------------------------------- 1 | 2 | GLOBAL 3 | {insert-credentials-id} 4 | {insert-credentials-description} 5 | {insert-user-name} 6 | {insert-user-password} 7 | 8 | -------------------------------------------------------------------------------- /jenkins/blue-green/add-blue-green-job.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function print_usage() { 4 | cat <&2 33 | print_usage 34 | exit -1 35 | fi 36 | } 37 | 38 | function run_util_script() { 39 | local script_path="$1" 40 | shift 41 | curl --silent "${artifacts_location}${script_path}${artifacts_location_sas_token}" | sudo bash -s -- "$@" 42 | local return_value=$? 43 | if [ $return_value -ne 0 ]; then 44 | >&2 echo "Failed while executing script '$script_path'." 45 | exit $return_value 46 | fi 47 | } 48 | 49 | #set defaults 50 | sp_credentials_id="sp" 51 | sp_credentials_desc="Service Principal to manage Azure resources" 52 | sp_environment="Azure" 53 | job_short_name="aks-blue-green-deployment" 54 | job_display_name="AKS Kubernetes Blue-green Deployment" 55 | job_description="A pipeline that demonstrates the blue-green deployment to AKS Kubernetes with the azure-acs Jenkins plugin." 56 | artifacts_location="https://raw.githubusercontent.com/Azure/azure-devops-utils/master/" 57 | 58 | while [[ $# > 0 ]] 59 | do 60 | key="$1" 61 | shift 62 | case $key in 63 | --jenkins_url|-j) 64 | jenkins_url="$1" 65 | shift 66 | ;; 67 | --jenkins_username|-ju) 68 | jenkins_username="$1" 69 | shift 70 | ;; 71 | --jenkins_password|-jp) 72 | jenkins_password="$1" 73 | shift 74 | ;; 75 | --aks_resource_group|-ag) 76 | aks_resource_group="$1" 77 | shift 78 | ;; 79 | --aks_name|-an) 80 | aks_name="$1" 81 | shift 82 | ;; 83 | --sp_credentials_id|-spi) 84 | sp_credentials_id="$1" 85 | shift 86 | ;; 87 | --sp_credentials_desc|-spd) 88 | sp_credentials_desc="$1" 89 | shift 90 | ;; 91 | --sp_subscription_id|-sps) 92 | sp_subscription_id="$1" 93 | shift 94 | ;; 95 | --sp_client_id|-spc) 96 | sp_client_id="$1" 97 | shift 98 | ;; 99 | --sp_client_password|-spp) 100 | sp_client_password="$1" 101 | shift 102 | ;; 103 | --sp_tenant_id|-spt) 104 | sp_tenant_id="$1" 105 | shift 106 | ;; 107 | --sp_environment|-spe) 108 | sp_environment="$1" 109 | shift 110 | ;; 111 | --job_short_name|-jsn) 112 | job_short_name="$1" 113 | shift 114 | ;; 115 | --job_display_name|-jdn) 116 | job_display_name="$1" 117 | shift 118 | ;; 119 | --job_description|-jd) 120 | job_description="$1" 121 | shift 122 | ;; 123 | --artifacts_location|-al) 124 | artifacts_location="$1" 125 | shift 126 | ;; 127 | --sas_token|-st) 128 | artifacts_location_sas_token="$1" 129 | shift 130 | ;; 131 | --help|-help|-h) 132 | print_usage 133 | exit 13 134 | ;; 135 | *) 136 | echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 137 | exit -1 138 | esac 139 | done 140 | 141 | throw_if_empty --jenkins_url "$jenkins_url" 142 | throw_if_empty --jenkins_username "$jenkins_username" 143 | if [ "$jenkins_username" != "admin" ]; then 144 | throw_if_empty --jenkins_password "$jenkins_password" 145 | fi 146 | throw_if_empty --aks_resource_group "$aks_resource_group" 147 | throw_if_empty --aks_name "$aks_name" 148 | throw_if_empty --sp_credentials_id "$sp_credentials_id" 149 | throw_if_empty --sp_subscription_id "$sp_subscription_id" 150 | throw_if_empty --sp_client_id "$sp_client_id" 151 | throw_if_empty --sp_client_password "$sp_client_password" 152 | throw_if_empty --sp_tenant_id "$sp_tenant_id" 153 | throw_if_empty --sp_environment "$sp_environment" 154 | 155 | #download dependencies 156 | job_xml=$(curl -s ${artifacts_location}/jenkins/blue-green/aks-k8s-blue-green-job.xml${artifacts_location_sas_token}) 157 | sp_credentials_xml=$(curl -s ${artifacts_location}/jenkins/blue-green/sp-credentials.xml${artifacts_location_sas_token}) 158 | 159 | #prepare job.xml 160 | job_xml=${job_xml//'{insert-job-display-name}'/${job_display_name}} 161 | job_xml=${job_xml//'{insert-job-description}'/${job_description}} 162 | job_xml=${job_xml//'{insert-aks-resource-group}'/${aks_resource_group}} 163 | job_xml=${job_xml//'{insert-aks-name}'/${aks_name}} 164 | job_xml=${job_xml//'{insert-artifacts-location}'/${artifacts_location}} 165 | job_xml=${job_xml//'{insert-sas-token}'/${artifacts_location_sas_token}} 166 | 167 | #prepare sp-credentials.xml 168 | sp_credentials_xml=${sp_credentials_xml//'{insert-sp-credentials-id}'/${sp_credentials_id}} 169 | sp_credentials_xml=${sp_credentials_xml//'{insert-sp-credentials-desc}'/${sp_credentials_desc}} 170 | sp_credentials_xml=${sp_credentials_xml//'{insert-sp-subscription-id}'/${sp_subscription_id}} 171 | sp_credentials_xml=${sp_credentials_xml//'{insert-sp-client-id}'/${sp_client_id}} 172 | sp_credentials_xml=${sp_credentials_xml//'{insert-sp-client-password}'/${sp_client_password}} 173 | sp_credentials_xml=${sp_credentials_xml//'{insert-sp-tenant-id}'/${sp_tenant_id}} 174 | sp_credentials_xml=${sp_credentials_xml//'{insert-sp-environment}'/${sp_environment}} 175 | 176 | #add Azure service principal credentials 177 | echo "${sp_credentials_xml}" >sp-credentials.xml 178 | run_util_script "jenkins/run-cli-command.sh" -j "$jenkins_url" -ju "$jenkins_username" -jp "$jenkins_password" -c 'create-credentials-by-xml SystemCredentialsProvider::SystemContextResolver::jenkins (global)' -cif "sp-credentials.xml" 179 | 180 | #add job 181 | echo "${job_xml}" >job.xml 182 | run_util_script "jenkins/run-cli-command.sh" -j "$jenkins_url" -ju "$jenkins_username" -jp "$jenkins_password" -c "create-job ${job_short_name}" -cif "job.xml" 183 | 184 | # clean up 185 | rm -f sp-credentials.xml job.xml 186 | -------------------------------------------------------------------------------- /jenkins/blue-green/aks-k8s-blue-green-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {insert-job-description} 5 | {insert-job-display-name} 6 | false 7 | 8 | 9 | 10 | 11 | TOMCAT_VERSION 12 | The Tomcat version that will be deployed and activated. 13 | 14 | 15 | 7-jre8 16 | 8-jre8 17 | 9-jre8 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 158 | true 159 | 160 | 161 | false 162 | 163 | -------------------------------------------------------------------------------- /jenkins/blue-green/bootstrap-k8s-blue-green.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function print_usage() { 4 | cat <&2 25 | print_usage 26 | exit -1 27 | fi 28 | } 29 | 30 | function run_util_script() { 31 | local script_path="$1" 32 | shift 33 | curl --silent "${artifacts_location}${script_path}${artifacts_location_sas_token}" | sudo bash -s -- "$@" 34 | local return_value=$? 35 | if [ $return_value -ne 0 ]; then 36 | >&2 echo "Failed while executing script '$script_path'." 37 | exit $return_value 38 | fi 39 | } 40 | 41 | #set defaults 42 | sp_environment=Azure 43 | artifacts_location="https://raw.githubusercontent.com/Azure/azure-devops-utils/master/" 44 | 45 | while [[ $# > 0 ]] 46 | do 47 | key="$1" 48 | shift 49 | case $key in 50 | --resource_group|-g) 51 | resource_group="$1" 52 | shift 53 | ;; 54 | --aks_name|-n) 55 | aks_name="$1" 56 | shift 57 | ;; 58 | --sp_subscription_id|-sps) 59 | sp_subscription_id="$1" 60 | shift 61 | ;; 62 | --sp_client_id|-spc) 63 | sp_client_id="$1" 64 | shift 65 | ;; 66 | --sp_client_password|-spp) 67 | sp_client_password="$1" 68 | shift 69 | ;; 70 | --sp_tenant_id|-spt) 71 | sp_tenant_id="$1" 72 | shift 73 | ;; 74 | --sp_environment|-spe) 75 | sp_environment="$1" 76 | shift 77 | ;; 78 | --artifacts_location|-al) 79 | artifacts_location="$1" 80 | shift 81 | ;; 82 | --sas_token|-st) 83 | artifacts_location_sas_token="$1" 84 | shift 85 | ;; 86 | --help|-help|-h) 87 | print_usage 88 | exit 13 89 | ;; 90 | *) 91 | echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 92 | exit -1 93 | esac 94 | done 95 | 96 | throw_if_empty --resource_group "$resource_group" 97 | throw_if_empty --aks_name "$aks_name" 98 | throw_if_empty --sp_subscription_id "$sp_subscription_id" 99 | throw_if_empty --sp_client_id "$sp_client_id" 100 | throw_if_empty --sp_client_password "$sp_client_password" 101 | throw_if_empty --sp_tenant_id "$sp_tenant_id" 102 | throw_if_empty --sp_environment "$sp_environment" 103 | 104 | run_util_script jenkins/blue-green/fetch-k8s-deployment-config.sh \ 105 | --artifacts_location "$artifacts_location" \ 106 | --sas_token "${artifacts_location_sas_token}" \ 107 | --directory k8s \ 108 | -f deployment.yml -f service.yml -f test-endpoint.yml 109 | 110 | sed -e 's/\${TARGET_ROLE}/blue/g; s/\${TOMCAT_VERSION}/7.0-jre8/g' k8s/deployment.yml >k8s/deployment-blue.yml 111 | sed -e 's/\${TARGET_ROLE}/green/g; s/\${TOMCAT_VERSION}/7.0-jre8/g' k8s/deployment.yml >k8s/deployment-green.yml 112 | sed -e 's/\${TARGET_ROLE}/blue/g; s/\${TOMCAT_VERSION}/7.0-jre8/g' k8s/service.yml >k8s/service-blue.yml 113 | sed -e 's/\${TARGET_ROLE}/blue/g' k8s/test-endpoint.yml >k8s/test-endpoint-blue.yml 114 | sed -e 's/\${TARGET_ROLE}/green/g' k8s/test-endpoint.yml >k8s/test-endpoint-green.yml 115 | 116 | az login --service-principal -u "$sp_client_id" -p "$sp_client_password" -t "$sp_tenant_id" 117 | az account set --subscription "$sp_subscription_id" 118 | az aks get-credentials --resource-group "${resource_group}" --name "${aks_name}" --admin --file kubeconfig 119 | 120 | kubectl --kubeconfig kubeconfig apply -f k8s/deployment-blue.yml 121 | kubectl --kubeconfig kubeconfig apply -f k8s/deployment-green.yml 122 | kubectl --kubeconfig kubeconfig apply -f k8s/service-blue.yml 123 | kubectl --kubeconfig kubeconfig apply -f k8s/test-endpoint-blue.yml 124 | kubectl --kubeconfig kubeconfig apply -f k8s/test-endpoint-green.yml 125 | 126 | function wait_public_ip() { 127 | # wait 10 minutes for the service endpoint public IP to be ready 128 | # it takes a long time for Azure to provision the frontend load balancer and the public IP address 129 | local name="$1" 130 | local count=0 131 | while true; do 132 | count=$(expr $count + 1) 133 | endpoint_ip=$(kubectl --kubeconfig=kubeconfig get services "$name" --output json | jq -r '.status.loadBalancer.ingress[0].ip') 134 | if [ "$endpoint_ip" != null ]; then 135 | echo "$name ip: $endpoint_ip" 136 | break 137 | fi 138 | if [ "$count" -gt 60 ]; then 139 | echo "Timeout while waiting for the $name IP" 140 | exit 1 141 | fi 142 | echo "$name IP not ready, sleep 10 seconds..." 143 | sleep 10 144 | done 145 | } 146 | 147 | wait_public_ip "tomcat-service" 148 | wait_public_ip "tomcat-test-blue" 149 | wait_public_ip "tomcat-test-green" 150 | 151 | # keep for diagnostics 152 | #rm -rf k8s 153 | 154 | az logout 155 | -------------------------------------------------------------------------------- /jenkins/blue-green/fetch-k8s-deployment-config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function print_usage() { 4 | cat <&2 20 | print_usage 21 | exit -1 22 | fi 23 | } 24 | 25 | function run_util_script() { 26 | local script_path="$1" 27 | shift 28 | curl --silent "${artifacts_location}${script_path}${artifacts_location_sas_token}" | sudo bash -s -- "$@" 29 | local return_value=$? 30 | if [ $return_value -ne 0 ]; then 31 | >&2 echo "Failed while executing script '$script_path'." 32 | exit $return_value 33 | fi 34 | } 35 | 36 | #set defaults 37 | config_files=() 38 | artifacts_location="https://raw.githubusercontent.com/Azure/azure-devops-utils/master/" 39 | artifacts_location_sas_token= 40 | 41 | while [[ $# > 0 ]] 42 | do 43 | key="$1" 44 | shift 45 | case $key in 46 | --file|-f) 47 | config_files+=("$1") 48 | shift 49 | ;; 50 | --directory|-d) 51 | directory="$1" 52 | shift 53 | ;; 54 | --artifacts_location|-al) 55 | artifacts_location="$1" 56 | shift 57 | ;; 58 | --sas_token|-st) 59 | artifacts_location_sas_token="$1" 60 | shift 61 | ;; 62 | --help|-help|-h) 63 | print_usage 64 | exit 13 65 | ;; 66 | *) 67 | echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 68 | exit -1 69 | esac 70 | done 71 | 72 | throw_if_empty --artifacts_location "$artifacts_location" 73 | if [[ "${#config_files[@]}" == 0 ]]; then 74 | config_files=(deployment.yml service.yml test-endpoint.yml) 75 | fi 76 | 77 | if [[ -n "$directory" ]]; then 78 | mkdir -p "$directory" 79 | cd "$directory" 80 | fi 81 | 82 | for file in "${config_files[@]}"; do 83 | wget -O "$file" "${artifacts_location}jenkins/blue-green/k8s/${file}${artifacts_location_sas_token}" 84 | done 85 | -------------------------------------------------------------------------------- /jenkins/blue-green/k8s/deployment.yml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: tomcat-deployment-${TARGET_ROLE} 5 | spec: 6 | replicas: 2 7 | template: 8 | metadata: 9 | labels: 10 | app: tomcat 11 | role: ${TARGET_ROLE} 12 | spec: 13 | containers: 14 | - name: tomcat-container 15 | image: tomcat:${TOMCAT_VERSION} 16 | ports: 17 | - containerPort: 8080 18 | readinessProbe: 19 | httpGet: 20 | path: / 21 | port: 8080 22 | strategy: 23 | type: RollingUpdate 24 | rollingUpdate: 25 | maxUnavailable: 1 26 | maxSurge: 50% 27 | -------------------------------------------------------------------------------- /jenkins/blue-green/k8s/service.yml: -------------------------------------------------------------------------------- 1 | kind: Service 2 | apiVersion: v1 3 | metadata: 4 | name: tomcat-service 5 | labels: 6 | app: tomcat 7 | role: ${TARGET_ROLE} 8 | version: ${TOMCAT_VERSION} 9 | env: prod 10 | spec: 11 | type: LoadBalancer 12 | selector: 13 | app: tomcat 14 | role: ${TARGET_ROLE} 15 | ports: 16 | - port: 80 17 | targetPort: 8080 18 | -------------------------------------------------------------------------------- /jenkins/blue-green/k8s/test-endpoint.yml: -------------------------------------------------------------------------------- 1 | kind: Service 2 | apiVersion: v1 3 | metadata: 4 | name: tomcat-test-${TARGET_ROLE} 5 | labels: 6 | app: tomcat 7 | role: test-${TARGET_ROLE} 8 | spec: 9 | type: LoadBalancer 10 | selector: 11 | app: tomcat 12 | role: ${TARGET_ROLE} 13 | ports: 14 | - port: 80 15 | targetPort: 8080 16 | -------------------------------------------------------------------------------- /jenkins/blue-green/sp-credentials.xml: -------------------------------------------------------------------------------- 1 | 2 | GLOBAL 3 | {insert-sp-credentials-id} 4 | {insert-sp-credentials-desc} 5 | 6 | {insert-sp-subscription-id} 7 | {insert-sp-client-id} 8 | {insert-sp-client-password} 9 | {insert-sp-tenant-id} 10 | {insert-sp-environment} 11 | 12 | -------------------------------------------------------------------------------- /jenkins/blue-green/ssh-credentials.xml: -------------------------------------------------------------------------------- 1 | 2 | GLOBAL 3 | {insert-ssh-credentials-id} 4 | {insert-ssh-credentials-desc} 5 | {insert-ssh-username} 6 | 7 | {insert-ssh-private-key} 8 | 9 | -------------------------------------------------------------------------------- /jenkins/blue-green/verified-jenkins-version: -------------------------------------------------------------------------------- 1 | 2.73.3 -------------------------------------------------------------------------------- /jenkins/init-aptly-repo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function print_usage() { 4 | cat <&2 19 | print_usage 20 | exit -1 21 | fi 22 | } 23 | 24 | # Set defaults 25 | repository_name="hello-karyon-rxnetty" 26 | 27 | while [[ $# > 0 ]] 28 | do 29 | key="$1" 30 | shift 31 | case $key in 32 | --vm_fqdn|-vf) 33 | vm_fqdn="$1";; 34 | --repository_name|-rn) 35 | repository_name="$1";; 36 | --help|-help|-h) 37 | print_usage 38 | exit 13;; 39 | *) 40 | echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 41 | exit -1 42 | esac 43 | shift 44 | done 45 | 46 | throw_if_empty vm_fqdn $vm_fqdn 47 | throw_if_empty repository_name $repository_name 48 | 49 | # Install aptly 50 | echo "deb http://repo.aptly.info/ squeeze main" | sudo tee -a "/etc/apt/sources.list" > /dev/null 51 | sudo apt-key adv --keyserver keys.gnupg.net --recv-keys 9E3E53F19C7DE460 52 | sudo DEBIAN_FRONTEND=noninteractive apt-get update --yes 53 | sudo DEBIAN_FRONTEND=noninteractive apt-get install aptly --force-yes --yes 54 | 55 | # Create default aptly repository 56 | sudo su -c "aptly repo create $repository_name" jenkins 57 | sudo su -c "aptly publish repo -architectures=\"amd64\" -component=main -distribution=trusty -skip-signing=true $repository_name" jenkins 58 | 59 | # Serve aptly on port 9999 60 | aptly_nginx_config=$(cat < /dev/null 74 | 75 | #restart nginx 76 | sudo service nginx restart -------------------------------------------------------------------------------- /jenkins/jenkins-on-azure/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-devops-utils/0ef03e153d984911f4646cea307d0278038b71c7/jenkins/jenkins-on-azure/copy.png -------------------------------------------------------------------------------- /jenkins/jenkins-on-azure/headshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-devops-utils/0ef03e153d984911f4646cea307d0278038b71c7/jenkins/jenkins-on-azure/headshot.png -------------------------------------------------------------------------------- /jenkins/jenkins-on-azure/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Jenkins On Azure 8 | 9 | 10 | 11 | 21 | 29 | 30 |
31 |
32 | 33 |
34 |
35 |

Jenkins On Azure

36 |
37 |

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

40 |

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

41 |

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

42 |

sudo apt-get purge nginx nginx-common

43 |
44 |
45 | 46 |
47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /jenkins/jenkins-on-azure/install-web-page.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | function print_usage() { 3 | cat <&2 19 | print_usage 20 | exit -1 21 | fi 22 | } 23 | 24 | #defaults 25 | artifacts_location="https://raw.githubusercontent.com/Azure/azure-devops-utils/master/" 26 | 27 | while [[ $# > 0 ]] 28 | do 29 | key="$1" 30 | shift 31 | case $key in 32 | --location|-l) 33 | location="$1" 34 | shift 35 | ;; 36 | --url|-u) 37 | url="$1" 38 | shift 39 | ;; 40 | --artifacts_location|-al) 41 | artifacts_location="$1" 42 | shift 43 | ;; 44 | --sas_token|-st) 45 | artifacts_location_sas_token="$1" 46 | shift 47 | ;; 48 | --help|-help|-h) 49 | print_usage 50 | exit 13 51 | ;; 52 | *) 53 | echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 54 | exit -1 55 | esac 56 | done 57 | 58 | throw_if_empty --location $location 59 | throw_if_empty --url $url 60 | 61 | #install jenkins-on-azure web page 62 | sudo mkdir ${location} 63 | artifacts=("headshot.png" "title.png" "azure.svg" "copy.png" "site.css" "site.js" "index.html") 64 | for i in "${artifacts[@]}"; do 65 | if [[ $i =~ .*html.* ]] 66 | then 67 | raw_resource=$(curl --silent "${artifacts_location}/jenkins/jenkins-on-azure/$i${artifacts_location_sas_token}") 68 | final_resource=${raw_resource//'{domain-name}'/${url}} 69 | echo "${final_resource}" | sudo tee ${location}/$i > /dev/null 70 | else 71 | curl --silent "${artifacts_location}/jenkins/jenkins-on-azure/$i${artifacts_location_sas_token}" -o ${location}/$i 72 | fi 73 | done 74 | -------------------------------------------------------------------------------- /jenkins/jenkins-on-azure/site.css: -------------------------------------------------------------------------------- 1 | html { 2 | position: relative; 3 | min-height: 100%; 4 | } 5 | body { 6 | margin: 0; 7 | padding: 0 0 40px 0; 8 | } 9 | img { 10 | vertical-align: middle; 11 | border: 0; 12 | } 13 | #page-head { 14 | box-sizing: border-box; 15 | } 16 | #header { 17 | background-color: #000000; 18 | height: 40px; 19 | } 20 | #header .logo { 21 | margin-left: 16px; 22 | } 23 | #jenkins-home-link { 24 | position: absolute; 25 | height: 40px; 26 | } 27 | #jenkins-head-icon { 28 | position: absolute; 29 | bottom: 0px; 30 | } 31 | #jenkins-name-icon { 32 | position: absolute; 33 | bottom: 3px; 34 | left: 32px; 35 | } 36 | #breadcrumbBar, #footer-container, .top-sticker-inner { 37 | background-color: #f6faf2; 38 | } 39 | .top-sticker, #top-sticker { 40 | width: 100%; 41 | z-index: 999; 42 | position: absolute; top: 40px; left: 0px; 43 | } 44 | #breadcrumbs { 45 | list-style-type: none; 46 | padding: 0 0 0 8px; 47 | margin: 0; 48 | height: 2em; 49 | } 50 | #breadcrumbs li { 51 | font-family: Helvetica, Arial, sans-serif; 52 | font-size: 14px; 53 | float: left; 54 | line-height: 2em; 55 | height: 2em; 56 | color: #555753; 57 | } 58 | #breadcrumbs li a:link, li a:visited { 59 | text-decoration: none; 60 | color: #555753; 61 | } 62 | #breadcrumbs a.inside { 63 | padding-right: 16px; 64 | } 65 | #breadcrumbs li a { 66 | display: block; 67 | padding-left: 0.75em; 68 | line-height: 2em; 69 | } 70 | 71 | 72 | 73 | #feature { 74 | margin: 0 auto 0 auto; 75 | overflow: hidden; 76 | position: absolute; 77 | width: 100%; 78 | } 79 | #image { 80 | position: fixed; 81 | top: 0; 82 | left: 0; 83 | z-index: -1; 84 | width: 100%; 85 | background-color: #0078d7; 86 | height: 100vh; 87 | overflow: hidden; 88 | } 89 | #image img { 90 | opacity: .23; 91 | height: 100%; 92 | /*max-height: 90vh;*/ 93 | display: block; 94 | margin: auto; 95 | } 96 | #content { 97 | display: block; 98 | margin: auto; 99 | font-family: "Segoe UI"; 100 | font-weight: normal; 101 | font-size: 4vmin; 102 | color: #fff; 103 | text-align: center; 104 | max-width: 90vw; 105 | } 106 | #title h1 { 107 | font-family: "Segoe UI Light"; 108 | color: #fff; 109 | font-weight: normal; 110 | font-size: 5.5vmin; 111 | margin: 4vmin; 112 | } 113 | #content_body { 114 | text-align: left; 115 | font-size: 3vmin; 116 | } 117 | #content_body p { 118 | padding-bottom: 0.3vmin; 119 | margin: 0.5vmin; 120 | } 121 | .snippet { 122 | margin-left: 6vmin; 123 | padding: 0.3vmin; 124 | background-color: #f6f8fa; 125 | font: 2vmin "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; 126 | color: #000; 127 | width: 100%; 128 | } 129 | #clipboard { 130 | height: 2vmin; 131 | margin-left: 1vmin; 132 | margin-right: 0.5vmin; 133 | cursor: pointer; 134 | } 135 | footer { 136 | padding: 11px 0; 137 | background-color: #f6faf2; 138 | border-top: 1px solid #d3d7cf; 139 | border-bottom: 1px solid #f6faf2; 140 | width: 100%; 141 | position: absolute; 142 | bottom: 0; 143 | left: 0; 144 | clear: both; 145 | font-size: 12px; 146 | text-align: right; 147 | } -------------------------------------------------------------------------------- /jenkins/jenkins-on-azure/site.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | $('.snippet').mouseenter(function() { 3 | $('#clipboard').remove(); 4 | span = $(this); 5 | temp = $(this).text(); 6 | snippetLen = $(this).text().length; 7 | $(this).find('img').show(); 8 | $(this).append( 9 | 'Copy to Clipboard'); 10 | 11 | $(this).parent().mouseleave(function() { 12 | $('#clipboard').remove(); 13 | }); 14 | 15 | $('#clipboard').click(function() { 16 | var doc = document, element = span[0], range, selection; 17 | if (doc.body.createTextRange) { 18 | range = document.body.createTextRange(); 19 | range.moveToElementText(element); 20 | range.select(); 21 | } else if (window.getSelection) { 22 | selection = window.getSelection(); 23 | range = document.createRange(); 24 | range.setStart(element, 0) 25 | range.setEndBefore($(this)[0]); 26 | selection.removeAllRanges(); 27 | selection.addRange(range); 28 | } 29 | document.execCommand('copy'); 30 | }); 31 | }); 32 | }); -------------------------------------------------------------------------------- /jenkins/jenkins-on-azure/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-devops-utils/0ef03e153d984911f4646cea307d0278038b71c7/jenkins/jenkins-on-azure/title.png -------------------------------------------------------------------------------- /jenkins/jenkins-verified-ver: -------------------------------------------------------------------------------- 1 | 2.73.3 2 | -------------------------------------------------------------------------------- /jenkins/run-cli-command.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function print_usage() { 4 | cat <&2 echo "Parameter '$name' cannot be empty." 21 | print_usage 22 | exit -1 23 | fi 24 | } 25 | 26 | #set defaults 27 | jenkins_url="http://localhost:8080/" 28 | jenkins_username="admin" 29 | 30 | while [[ $# > 0 ]] 31 | do 32 | key="$1" 33 | shift 34 | case $key in 35 | --command|-c) 36 | command="$1" 37 | shift 38 | ;; 39 | --command_input_file|-cif) 40 | command_input_file="$1" 41 | shift 42 | ;; 43 | --jenkins_url|-j) 44 | jenkins_url="$1" 45 | shift 46 | ;; 47 | --jenkins_username|-ju) 48 | jenkins_username="$1" 49 | shift 50 | ;; 51 | --jenkins_password|-jp) 52 | jenkins_password="$1" 53 | shift 54 | ;; 55 | --help|-help|-h) 56 | print_usage 57 | exit 13 58 | ;; 59 | *) 60 | >&2 echo "ERROR: Unknown argument '$key' to script '$0'" 61 | exit -1 62 | esac 63 | done 64 | 65 | throw_if_empty jenkins_username $jenkins_username 66 | if [ "$jenkins_username" != "admin" ]; then 67 | throw_if_empty jenkins_password $jenkins_password 68 | fi 69 | 70 | function retry_until_successful { 71 | counter=0 72 | "${@}" 73 | while [ $? -ne 0 ]; do 74 | if [[ "$counter" -gt 20 ]]; then 75 | exit 1 76 | else 77 | let counter++ 78 | fi 79 | sleep 5 80 | "${@}" 81 | done; 82 | } 83 | 84 | function retry_until_successful_with_input { 85 | input_file=$1 86 | shift 87 | counter=0 88 | cat "$input_file" | "${@}" 89 | while [ $? -ne 0 ]; do 90 | if [[ "$counter" -gt 20 ]]; then 91 | exit 1 92 | else 93 | let counter++ 94 | fi 95 | sleep 5 96 | cat "$input_file" | "${@}" 97 | done; 98 | } 99 | 100 | if [ ! -e jenkins-cli.jar ]; then 101 | >&2 echo "Downloading Jenkins CLI..." 102 | retry_until_successful wget ${jenkins_url}jnlpJars/jenkins-cli.jar -O jenkins-cli.jar 103 | fi 104 | 105 | if [ -z "$jenkins_password" ]; then 106 | # NOTE: Intentionally setting this after the first retry_until_successful to ensure the initialAdminPassword file exists 107 | jenkins_password=`sudo cat /var/lib/jenkins/secrets/initialAdminPassword` 108 | fi 109 | 110 | >&2 echo "Running \"${command}\"..." 111 | if [ -z "${command_input_file}" ]; then 112 | retry_until_successful java -jar jenkins-cli.jar -s "${jenkins_url}" -auth "${jenkins_username}":"${jenkins_password}" $command 113 | else 114 | retry_until_successful_with_input "${command_input_file}" java -jar jenkins-cli.jar -s "${jenkins_url}" -auth "${jenkins_username}":"${jenkins_password}" $command 115 | fi -------------------------------------------------------------------------------- /jenkins/unsecure-jenkins-instance.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo echo "1" > /var/lib/jenkins/jenkins.install.InstallUtil.lastExecVersion 4 | 5 | unsecure_config_xml=$(sed -zr \ 6 | -e "s|.*|false|"\ 7 | -e "s|||"\ 8 | -e "s|||"\ 9 | /var/lib/jenkins/config.xml) 10 | 11 | echo "${unsecure_config_xml}" > /var/lib/jenkins/config.xml 12 | 13 | sudo service jenkins restart 14 | -------------------------------------------------------------------------------- /powershell/Jenkins-Windows-Init-Script-SSH.ps1: -------------------------------------------------------------------------------- 1 | # Download and Install Java 2 | Set-ExecutionPolicy Unrestricted 3 | #Default workspace location 4 | Set-Location C:\ 5 | $source = "http://download.oracle.com/otn-pub/java/jdk/8u131-b11/d54c1d3a095b4ff2b6607d096fa80163/jdk-8u131-windows-x64.exe" 6 | $destination = "C:\jdk-8u131-windows-x64.exe" 7 | $client = new-object System.Net.WebClient 8 | $cookie = "oraclelicense=accept-securebackup-cookie" 9 | $client.Headers.Add([System.Net.HttpRequestHeader]::Cookie, $cookie) 10 | $client.downloadFile($source, $destination) 11 | $proc = Start-Process -FilePath $destination -ArgumentList "/s" -Wait -PassThru 12 | $proc.WaitForExit() 13 | [System.Environment]::SetEnvironmentVariable("JAVA_HOME", "c:\Program Files\Java\jdk1.8.0_131", "Machine") 14 | [System.Environment]::SetEnvironmentVariable("PATH", $Env:Path + ";c:\Program Files\Java\jdk1.8.0_131\bin", "Machine") 15 | $Env:Path += ";c:\Program Files\Java\jdk1.8.0_131\bin" 16 | #Disable git credential manager, get more details in https://support.cloudbees.com/hc/en-us/articles/221046888-Build-Hang-or-Fail-with-Git-for-Windows 17 | git config --system --unset credential.helper 18 | 19 | 20 | # Install Maven 21 | $source = "https://archive.apache.org/dist/maven/maven-3/3.5.2/binaries/apache-maven-3.5.2-bin.zip" 22 | $destination = "C:\maven.zip" 23 | $webClient = New-Object System.Net.WebClient 24 | $webClient.DownloadFile($source, $destination) 25 | $shell_app=new-object -com shell.application 26 | $zip_file = $shell_app.namespace($destination) 27 | mkdir 'C:\Program Files\apache-maven-3.5.2' 28 | $destination = $shell_app.namespace('C:\Program Files') 29 | $destination.Copyhere($zip_file.items(), 0x14) 30 | [System.Environment]::SetEnvironmentVariable("PATH", $Env:Path + ";C:\Program Files\apache-maven-3.5.2\bin", "Machine") 31 | $Env:Path += ";C:\Program Files\apache-maven-3.5.2\bin" 32 | 33 | 34 | # Install Git 35 | $source = "https://github.com/git-for-windows/git/releases/latest" 36 | $latestRelease = Invoke-WebRequest -UseBasicParsing $source -Headers @{"Accept"="application/json"} 37 | $json = $latestRelease.Content | ConvertFrom-Json 38 | $latestVersion = $json.tag_name 39 | $versionHead = $latestVersion.Substring(1, $latestVersion.IndexOf("windows")-2) 40 | $source = "https://github.com/git-for-windows/git/releases/download/v${versionHead}.windows.1/Git-${versionHead}-64-bit.exe" 41 | $destination = "C:\Git-${versionHead}-64-bit.exe" 42 | $webClient = New-Object System.Net.WebClient 43 | $webClient.DownloadFile($source, $destination) 44 | $proc = Start-Process -FilePath $destination -ArgumentList "/VERYSILENT" -Wait -PassThru 45 | $proc.WaitForExit() 46 | $Env:Path += ";C:\Program Files\Git\cmd" -------------------------------------------------------------------------------- /powershell/Jenkins-Windows-Init-Script-no-secrets.ps1: -------------------------------------------------------------------------------- 1 | # Download and Install Java 2 | Set-ExecutionPolicy Unrestricted 3 | #Default workspace location 4 | Set-Location C:\ 5 | $source = "http://download.oracle.com/otn-pub/java/jdk/8u131-b11/d54c1d3a095b4ff2b6607d096fa80163/jdk-8u131-windows-x64.exe" 6 | $destination = "C:\jdk-8u131-windows-x64.exe" 7 | $client = new-object System.Net.WebClient 8 | $cookie = "oraclelicense=accept-securebackup-cookie" 9 | $client.Headers.Add([System.Net.HttpRequestHeader]::Cookie, $cookie) 10 | $client.downloadFile($source, $destination) 11 | $proc = Start-Process -FilePath $destination -ArgumentList "/s" -Wait -PassThru 12 | $proc.WaitForExit() 13 | $javaHome = "c:\Program Files\Java\jdk1.8.0_131" 14 | [System.Environment]::SetEnvironmentVariable("JAVA_HOME", ${javaHome}, "Machine") 15 | [System.Environment]::SetEnvironmentVariable("PATH", $Env:Path + ";${javaHome}\bin", "Machine") 16 | $Env:Path += ";${javaHome}\bin" 17 | 18 | 19 | # Install Maven 20 | $source = "https://archive.apache.org/dist/maven/maven-3/3.5.2/binaries/apache-maven-3.5.2-bin.zip" 21 | $destination = "C:\maven.zip" 22 | $webClient = New-Object System.Net.WebClient 23 | $webClient.DownloadFile($source, $destination) 24 | $shell_app=new-object -com shell.application 25 | $zip_file = $shell_app.namespace($destination) 26 | mkdir 'C:\Program Files\apache-maven-3.5.2' 27 | $destination = $shell_app.namespace('C:\Program Files') 28 | $destination.Copyhere($zip_file.items(), 0x14) 29 | [System.Environment]::SetEnvironmentVariable("PATH", $Env:Path + ";C:\Program Files\apache-maven-3.5.2\bin", "Machine") 30 | $Env:Path += ";C:\Program Files\apache-maven-3.5.2\bin" 31 | 32 | 33 | # Install Git 34 | $source = "https://github.com/git-for-windows/git/releases/latest" 35 | $latestRelease = Invoke-WebRequest -UseBasicParsing $source -Headers @{"Accept"="application/json"} 36 | $json = $latestRelease.Content | ConvertFrom-Json 37 | $latestVersion = $json.tag_name 38 | $versionHead = $latestVersion.Substring(1, $latestVersion.IndexOf("windows")-2) 39 | $source = "https://github.com/git-for-windows/git/releases/download/v${versionHead}.windows.1/Git-${versionHead}-64-bit.exe" 40 | $destination = "C:\Git-${versionHead}-64-bit.exe" 41 | $webClient = New-Object System.Net.WebClient 42 | $webClient.DownloadFile($source, $destination) 43 | $proc = Start-Process -FilePath $destination -ArgumentList "/VERYSILENT" -Wait -PassThru 44 | $proc.WaitForExit() 45 | $Env:Path += ";C:\Program Files\Git\cmd" 46 | #Disable git credential manager, get more details in https://support.cloudbees.com/hc/en-us/articles/221046888-Build-Hang-or-Fail-with-Git-for-Windows 47 | git config --system --unset credential.helper 48 | 49 | 50 | # Install Slaves jar and connect via JNLP 51 | # Jenkins plugin will dynamically pass the server name and vm name. 52 | # If your jenkins server is configured for security , make sure to edit command for how slave executes 53 | $jenkinsserverurl = $args[0] 54 | $vmname = $args[1] 55 | $secret = $args[2] 56 | 57 | # Downloading jenkins slaves jar 58 | Write-Output "Downloading jenkins slave jar " 59 | mkdir c:\jenkins 60 | $slaveSource = $jenkinsserverurl + "jnlpJars/slave.jar" 61 | $destSource = "c:\jenkins\slave.jar" 62 | $wc = New-Object System.Net.WebClient 63 | $wc.DownloadFile($slaveSource, $destSource) 64 | 65 | # Download the service wrapper 66 | $wrapperExec = "c:\jenkins\jenkins-slave.exe" 67 | $configFile = "c:\jenkins\jenkins-slave.xml" 68 | $wc.DownloadFile("https://github.com/kohsuke/winsw/releases/download/winsw-v2.1.2/WinSW.NET2.exe", $wrapperExec) 69 | $wc.DownloadFile("https://raw.githubusercontent.com/azure-devops/ci/master/resources/jenkins-slave.exe.config", "c:\jenkins\jenkins-slave.exe.config") 70 | $wc.DownloadFile("https://raw.githubusercontent.com/azure-devops/ci/master/resources/jenkins-slave.xml", $configFile) 71 | 72 | # Prepare config 73 | Write-Output "Executing agent process " 74 | $configExec = "${javaHome}\bin\java.exe" 75 | $configArgs = "-jnlpUrl `"${jenkinsserverurl}/computer/${vmname}/slave-agent.jnlp`" -noReconnect" 76 | if ($secret) { 77 | $configArgs += " -secret `"$secret`"" 78 | } 79 | (Get-Content $configFile).replace('@JAVA@', $configExec) | Set-Content $configFile 80 | (Get-Content $configFile).replace('@ARGS@', $configArgs) | Set-Content $configFile 81 | (Get-Content $configFile).replace('@SLAVE_JAR_URL', $slaveSource) | Set-Content $configFile 82 | 83 | # Install the service 84 | & $wrapperExec install 85 | & $wrapperExec start -------------------------------------------------------------------------------- /powershell/Jenkins-Windows-Init-Script.ps1: -------------------------------------------------------------------------------- 1 | Set-ExecutionPolicy Unrestricted 2 | # Jenkins plugin will dynamically pass the server name and vm name. 3 | # If your jenkins server is configured for security , make sure to edit command for how slave executes 4 | # You may need to pass credentails or secret in the command , Refer to help by running "java -jar slave.jar --help" 5 | $jenkinsserverurl = $args[0] 6 | $vmname = $args[1] 7 | 8 | 9 | # Download the file to a specific location 10 | Write-Output "Downloading zulu SDK " 11 | $source = "http://azure.azulsystems.com/zulu/zulu1.7.0_51-7.3.0.4-win64.zip?jenkins" 12 | mkdir d:\azurecsdir 13 | $destination = "d:\azurecsdir\zuluJDK.zip" 14 | $wc = New-Object System.Net.WebClient 15 | $wc.DownloadFile($source, $destination) 16 | 17 | Write-Output "Unzipping JDK " 18 | # Unzip the file to specified location 19 | $shell_app=new-object -com shell.application 20 | $zip_file = $shell_app.namespace($destination) 21 | mkdir d:\java 22 | $destination = $shell_app.namespace("d:\java") 23 | $destination.Copyhere($zip_file.items()) 24 | Write-Output "Successfully downloaded and extracted JDK " 25 | 26 | # Downloading jenkins slaves jar 27 | Write-Output "Downloading jenkins slave jar " 28 | $slaveSource = $jenkinsserverurl + "jnlpJars/slave.jar" 29 | $destSource = "d:\java\slave.jar" 30 | $wc = New-Object System.Net.WebClient 31 | $wc.DownloadFile($slaveSource, $destSource) 32 | 33 | # execute slave 34 | Write-Output "Executing slave process " 35 | $java="d:\java\zulu1.7.0_51-7.3.0.4-win64\bin\java.exe" 36 | $jar="-jar" 37 | $jnlpUrl="-jnlpUrl" 38 | $serverURL=$jenkinsserverurl+"computer/" + $vmname + "/slave-agent.jnlp" 39 | $jnlpCredentialsFlag="-jnlpCredentials" 40 | # syntax for credentials username:apitoken or username:password 41 | # you can get api token by clicking on your username --> configure --> show api token 42 | $credentials="username:apitoken" 43 | & $java $jar $destSource $jnlpCredentialsFlag $credentials $jnlpUrl $serverURL 44 | 45 | -------------------------------------------------------------------------------- /powershell/Migrate-Image-From-Classic.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Migrate-Image-From-Classic.ps1 4 | .DESCRIPTION 5 | Migrates an image from the classic image store to RM 6 | .PARAMETER ImageName 7 | Original image name 8 | .PARAMETER TargetStorageAccount 9 | Target account to copy to 10 | .PARAMETER TargetResourceGroup 11 | Resource group of the target storage account 12 | .PARAMETER TargetContainer 13 | Target container to put the VHD 14 | .PARAMETER TargetVirtualPath 15 | Virtual path to put the blob in. If not specified, defaults to the virtual path of the source URI 16 | .PARAMETER TargetBlobName 17 | Blob name to copy to. If not specified, defaults to the blob name of the source URI 18 | #> 19 | 20 | param ( 21 | [Parameter(Mandatory=$true)] 22 | [string]$ImageName, 23 | [Parameter(Mandatory=$true)] 24 | [string]$TargetBlob, 25 | [Parameter(Mandatory=$true)] 26 | $TargetStorageAccount = $null 27 | [Parameter(Mandatory=$true)] 28 | [string]$TargetResourceGroup, 29 | [string]$TargetContainer = 'system', 30 | [Parameter(Mandatory=$true)] 31 | [string]$TargetVirtualPath = 'Microsoft.Compute/Images/custom' 32 | ) 33 | 34 | # Ensure logged in 35 | $ctx = Get-AzureRmContext 36 | if (!$ctx) { 37 | Exit 38 | } 39 | 40 | # First find the VM image and determine what account it is in. 41 | Write-Output "Looking up image $ImageName" 42 | $image = Get-AzureVMImage $ImageName 43 | 44 | if (!$image) { 45 | throw "Could not find $ImageName" 46 | } 47 | 48 | # Determine the current storage account location. 49 | $vhdURI = $null 50 | if ($image.MediaLink) { 51 | # Contains the media link URL in the image info, rather than the os disk config. Parse this 52 | $vhdURI = $image.MediaLink 53 | } 54 | else { 55 | $vhdURI = $image.OSDiskConfiguration.MediaLink.AbsoluteUri 56 | } 57 | 58 | # Parse out the source URI info 59 | $uriInfo = .\Parse-VHD-Uri.ps1 $vhdURI -ErrorAction Stop 60 | 61 | $sourceStorageAccountName = $uriInfo.StorageAccount 62 | $sourceStorageContainer = $uriInfo.ContainerName 63 | $sourceStorageBlob = $uriInfo.Blob 64 | $sourceStorageVirtualPath = $uriInfo.VirtualPath 65 | 66 | Write-Output "Copy details:" 67 | Write-Output " Storage Account $sourceStorageAccountName -> $TargetStorageAccount" 68 | Write-Output " Container $sourceStorageContainer -> $TargetContainer" 69 | if ($TargetVirtualPath -or $sourceStorageVirtualPath) { 70 | Write-Output " Virtual Path $sourceStorageVirtualPath -> $TargetVirtualPath" 71 | } 72 | Write-Output " Blob $sourceStorageBlob -> $TargetBlob" 73 | 74 | # Validate that we got the storage account right 75 | $storageAccount = Get-AzureStorageAccount $sourceStorageAccountName 76 | 77 | if (!$storageAccount) { 78 | throw "Could not find storage account $sourceStorageAccountName" 79 | } 80 | 81 | # Generate the source and target contexts 82 | $sourceKey = Get-AzureStorageKey -StorageAccountName $sourceStorageAccountName 83 | $sourceContext = New-AzureStorageContext -StorageAccountName $sourceStorageAccountName -StorageAccountKey $sourceKey.Primary 84 | 85 | # Grab the source blob 86 | $sourceBlob = $null 87 | if ($sourceStorageVirtualPath) { 88 | $sourceBlob = Get-AzureStorageBlob -Blob "$sourceStorageVirtualPath/$sourceStorageBlob" -Container $sourceStorageContainer -Context $sourceContext -ErrorAction SilentlyContinue 89 | } 90 | else { 91 | $sourceBlob = Get-AzureStorageBlob -Blob $sourceStorageBlob -Container $sourceStorageContainer -Context $sourceContext -ErrorAction SilentlyContinue 92 | } 93 | if (!$sourceBlob) { 94 | Write-Error "Could not locate source blob $sourceStorageBlob" 95 | Exit 96 | } 97 | 98 | if ($TargetVirtualPath) { 99 | $targetUri = "https://$TargetStorageAccount.blob.core.windows.net/$TargetContainer/$TargetVirtualPath/$TargetBlob" 100 | } 101 | else { 102 | $targetUri = "https://$TargetStorageAccount.blob.core.windows.net/$TargetContainer/$TargetBlob" 103 | } 104 | 105 | # Locate the storage account key for the target 106 | $targetStorageAccountKeys = Get-AzureRmStorageAccountKey $TargetStorageAccount -ResourceGroupName $TargetResourceGroup -ErrorAction SilentlyContinue 107 | if ($targetStorageAccountKeys) { 108 | if ($targetStorageAccountKeys[0]) { 109 | $targetStorageAccountKey = $targetStorageAccountKeys[0].Value 110 | } 111 | else { 112 | $targetStorageAccountKey = $targetStorageAccountKeys.Key1 113 | } 114 | } 115 | if (!$targetStorageAccountKey) { 116 | Write-Error "Could not find target storage account $TargetStorageAccount or locate the storage key, skipping" 117 | Exit 118 | } 119 | 120 | # Create a storage context for the target 121 | $targetContext = New-AzureStorageContext -StorageAccountName $TargetStorageAccount -StorageAccountKey $targetStorageAccountKey 122 | 123 | # Ensure that the container is created if not existing 124 | $existingContainer = Get-AzureStorageContainer -Context $targetContext -Name $TargetContainer -ErrorAction SilentlyContinue 125 | 126 | if (!$existingContainer) { 127 | Write-Output "Target storage container $TargetContainer doesn't exist, creating" 128 | New-AzureStorageContainer -Context $targetContext -Name $TargetContainer 129 | } 130 | 131 | $fullTargetBlobName = $TargetBlob 132 | if ($TargetVirtualPath) { 133 | $fullTargetBlobName = "$TargetVirtualPath/$TargetBlob" 134 | } 135 | 136 | $blobCopy = Start-AzureStorageBlobCopy -CloudBlob $sourceBlob.ICloudBlob -Context $sourceContext -DestContext $targetContext -DestContainer $TargetContainer -DestBlob $fullTargetBlobName 137 | 138 | Write-Output "Started $vhdURI -> $targetUri" 139 | 140 | # Waiting till all copies done 141 | $allFinished = $false 142 | while (!$allFinished) { 143 | $allFinished = $true 144 | $blobCopyState = $blobCopy | Get-AzureStorageBlobCopyState 145 | if ($blobCopyState.Status -eq "Pending") 146 | { 147 | $allFinished = $false 148 | $percent = ($blobCopyState.BytesCopied * 100) / $blobCopyState.TotalBytes 149 | $percent = [math]::round($percent,2) 150 | $blobCopyName = $blobCopyState.CopyId 151 | Write-Progress -Id 0 -Activity "Copying from classic... " -PercentComplete $percent -CurrentOperation "Copying $blobCopyName" 152 | } 153 | Start-Sleep -s 30 154 | } 155 | 156 | Write-Output "All operations complete" -------------------------------------------------------------------------------- /powershell/README.md: -------------------------------------------------------------------------------- 1 | ## Jenkins Azure Agent initialization script 2 | > [Jenkins-Windows-Init-Script.ps1](Jenkins-Windows-Init-Script.ps1) 3 | 4 | Sample script on how to setup your Windows Azure Jenkins Agent to communicate through JNLP with the Jenkins master. 5 | Before deploying using this initialization script remember to update this line with your credentials (you can get api token by clicking on your username --> configure --> show api token): 6 | ```powershell 7 | $credentials="username:apitoken" 8 | ``` 9 | More information about the Azure VM Agents plugin can be found [here](https://wiki.jenkins-ci.org/display/JENKINS/Azure+VM+Agents+Plugin). 10 | 11 | ## Azure Classic VM Image migration 12 | > [Migrate-Image-From-Classic.ps1](Migrate-Image-From-Classic.ps1) 13 | 14 | Migrates an image from the classic image model to the new Azure Resource Manager model. 15 | 16 | | Argument | Description | 17 | |----------------------|---------------------------------------------------------------------------------------------------| 18 | | ImageName | Original image name | 19 | | TargetStorageAccount | Target account to copy to | 20 | | TargetResourceGroup | Resource group of the target storage account | 21 | | TargetContainer | Target container to put the VHD | 22 | | TargetVirtualPath | Virtual path to put the blob in. If not specified, defaults to the virtual path of the source URI | 23 | | TargetBlobName | Blob name to copy to. If not specified, defaults to the blob name of the source URI | 24 | 25 | ## Questions/Comments? azdevopspub@microsoft.com -------------------------------------------------------------------------------- /quickstart_template/101-spinnaker-aks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function print_usage() { 4 | cat <&2 32 | print_usage 33 | exit -1 34 | fi 35 | } 36 | 37 | function run_util_script() { 38 | local script_path="$1" 39 | shift 40 | curl --silent "${artifacts_location}${script_path}${artifacts_location_sas_token}" | sudo bash -s -- "$@" 41 | local return_value=$? 42 | if [ $return_value -ne 0 ]; then 43 | >&2 echo "Failed while executing script '$script_path'." 44 | exit $return_value 45 | fi 46 | } 47 | 48 | function install_az() { 49 | if !(command -v az >/dev/null); then 50 | sudo apt-get update 51 | sudo apt-get install curl apt-transport-https lsb-release gnupg -y 52 | curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc.gpg > /dev/null 53 | AZ_REPO=$(lsb_release -cs) 54 | echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $AZ_REPO main" | sudo tee /etc/apt/sources.list.d/azure-cli.list 55 | sudo apt-get update 56 | sudo apt-get install azure-cli 57 | fi 58 | } 59 | 60 | # Set defaults 61 | artifacts_location="https://raw.githubusercontent.com/azure/azure-devops-utils/master/" 62 | artifacts_location_sas_token="" 63 | 64 | while [[ $# > 0 ]] 65 | do 66 | key="$1" 67 | shift 68 | case $key in 69 | --app_id|-ai) 70 | app_id="$1";; 71 | --app_key|-ak) 72 | app_key="$1";; 73 | --username|-u) 74 | username="$1";; 75 | --tenant_id|-ti) 76 | tenant_id="$1";; 77 | --subscription_id|-si) 78 | subscription_id="$1";; 79 | --resource_group|-rg) 80 | resource_group="$1";; 81 | --vault_name|-vn) 82 | vault_name="$1";; 83 | --storage_account_name|-san) 84 | storage_account_name="$1";; 85 | --storage_account_key|-sak) 86 | storage_account_key="$1";; 87 | --aks_cluster_name|-acn) 88 | aks_cluster_name="$1";; 89 | --aks_resource_group|-arg) 90 | aks_resource_group="$1";; 91 | --use_ssh_public_key|-uspk) 92 | use_ssh_public_key="$1";; 93 | --region|-r) 94 | region="$1";; 95 | --artifacts_location|-al) 96 | artifacts_location="$1";; 97 | --sas_token|-st) 98 | artifacts_location_sas_token="$1";; 99 | --help|-help|-h) 100 | print_usage 101 | exit 13;; 102 | *) 103 | echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 104 | exit -1 105 | esac 106 | shift 107 | done 108 | 109 | throw_if_empty app_id $app_id 110 | throw_if_empty app_key $app_key 111 | throw_if_empty username $username 112 | throw_if_empty tenant_id $tenant_id 113 | throw_if_empty subscription_id $subscription_id 114 | throw_if_empty resource_group $resource_group 115 | throw_if_empty vault_name $vault_name 116 | throw_if_empty storage_account_name $storage_account_name 117 | throw_if_empty storage_account_key $storage_account_key 118 | throw_if_empty region $region 119 | throw_if_empty aks_cluster_name $aks_cluster_name 120 | throw_if_empty aks_resource_group $aks_resource_group 121 | throw_if_empty use_ssh_public_key $use_ssh_public_key 122 | 123 | #install az and hal 124 | install_az 125 | run_util_script "spinnaker/install_halyard/install_halyard.sh" -san "$storage_account_name" -sak "$storage_account_key" -u "$username" 126 | 127 | #get-credentials from aks 128 | az login --service-principal -u $app_id -p $app_key -t $tenant_id 129 | az aks get-credentials --resource-group $aks_resource_group --name $aks_cluster_name -f /home/$username/.kube/config 130 | chmod 777 /home/$username/.kube/config 131 | 132 | #install kubectl 133 | curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl 134 | chmod +x ./kubectl 135 | sudo mv ./kubectl /usr/local/bin/kubectl 136 | 137 | # Configure Azure provider for Spinnaker 138 | echo "$app_key" | hal config provider azure account add my-azure-account \ 139 | --client-id "$app_id" \ 140 | --tenant-id "$tenant_id" \ 141 | --subscription-id "$subscription_id" \ 142 | --default-key-vault "$vault_name" \ 143 | --default-resource-group "$resource_group" \ 144 | --packer-resource-group "$resource_group" \ 145 | --useSshPublicKey "$use_ssh_public_key" \ 146 | --app-key 147 | 148 | #change region if region not in eastus or westus 149 | if [ "$region" != eastus ] && [ "$region" != westus ]; then 150 | hal config provider azure account edit my-azure-account \ 151 | --regions "eastus","westus","$region" 152 | fi 153 | hal config provider azure enable 154 | 155 | # Configure kubernetes provider for Spinnaker 156 | echo "$app_key" | hal config provider kubernetes account add my-k8s-v2-account \ 157 | --provider-version v2 \ 158 | --context $aks_cluster_name 159 | hal config provider kubernetes account edit my-k8s-v2-account --kubeconfig-file /home/$username/.kube/config 160 | hal config provider kubernetes enable 161 | hal config features edit --artifacts true 162 | hal config deploy edit --type distributed --account-name my-k8s-v2-account 163 | 164 | # Deploy Spinnaker to aks 165 | sudo hal deploy apply 166 | 167 | # Connect to Spinnaker 168 | echo "Connecting to Spinnaker..." 169 | hal deploy connect &>/dev/null & 170 | # Wait for connection 171 | echo "while !(nc -z localhost 8084) || !(nc -z localhost 9000); do sleep 1; done" | timeout 20 bash 172 | if [ $? -ne 0 ]; then 173 | echo "Failed to connect to Spinnaker." 174 | else 175 | echo "Successfully connected to Spinnaker." 176 | fi 177 | -------------------------------------------------------------------------------- /quickstart_template/101-spinnaker-install-selection.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function print_usage() { 4 | cat <&2 34 | print_usage 35 | exit -1 36 | fi 37 | } 38 | 39 | function run_util_script() { 40 | local script_path="$1" 41 | shift 42 | curl --silent "${artifacts_location}${script_path}${artifacts_location_sas_token}" | sudo bash -s -- "$@" 43 | local return_value=$? 44 | if [ $return_value -ne 0 ]; then 45 | >&2 echo "Failed while executing script '$script_path'." 46 | exit $return_value 47 | fi 48 | } 49 | 50 | # Set defaults 51 | artifacts_location="https://raw.githubusercontent.com/Azure/azure-devops-utils/master/" 52 | artifacts_location_sas_token="" 53 | 54 | while [[ $# > 0 ]] 55 | do 56 | key="$1" 57 | shift 58 | case $key in 59 | --app_id|-ai) 60 | app_id="$1";; 61 | --app_key|-ak) 62 | app_key="$1";; 63 | --username|-u) 64 | username="$1";; 65 | --jenkins_password|-jp) 66 | jenkins_password="$1";; 67 | --tenant_id|-ti) 68 | tenant_id="$1";; 69 | --subscription_id|-si) 70 | subscription_id="$1";; 71 | --resource_group|-rg) 72 | resource_group="$1";; 73 | --vault_name|-vn) 74 | vault_name="$1";; 75 | --storage_account_name|-san) 76 | storage_account_name="$1";; 77 | --storage_account_key|-sak) 78 | storage_account_key="$1";; 79 | --aks_cluster_name|-acn) 80 | aks_cluster_name="$1";; 81 | --aks_resource_group|-arg) 82 | aks_resource_group="$1";; 83 | --region|-r) 84 | region="$1";; 85 | --vm_fqdn|-vf) 86 | vm_fqdn="$1";; 87 | --use_ssh_public_key|-uspk) 88 | use_ssh_public_key="$1";; 89 | --artifacts_location|-al) 90 | artifacts_location="$1";; 91 | --sas_token|-st) 92 | artifacts_location_sas_token="$1";; 93 | --help|-help|-h) 94 | print_usage 95 | exit 13;; 96 | *) 97 | echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 98 | exit -1 99 | esac 100 | shift 101 | done 102 | 103 | throw_if_empty app_id $app_id 104 | throw_if_empty app_key $app_key 105 | throw_if_empty username $username 106 | throw_if_empty tenant_id $tenant_id 107 | throw_if_empty subscription_id $subscription_id 108 | throw_if_empty resource_group $resource_group 109 | throw_if_empty vault_name $vault_name 110 | throw_if_empty storage_account_name $storage_account_name 111 | throw_if_empty storage_account_key $storage_account_key 112 | throw_if_empty vm_fqdn $vm_fqdn 113 | throw_if_empty use_ssh_public_key $use_ssh_public_key 114 | 115 | #installed on the vmss if there is no aks_cluster_name, otherwise installed on aks 116 | if [ -z "$aks_cluster_name" ] 117 | then 118 | run_util_script "quickstart_template/301-jenkins-aptly-spinnaker-vmss.sh" -ju "$username" -jp "$jenkins_password" -ai "$app_id" -ak "$app_key" -ti "$tenant_id" -si "$subscription_id" -rg "$resource_group" -vn "$vault_name" -san "$storage_account_name" -sak "$storage_account_key" -vf "$vm_fqdn" -r "$region" -al "$artifacts_location" -st "$artifacts_location_sas_token" -uspk "$use_ssh_public_key" 119 | else 120 | run_util_script "quickstart_template/101-spinnaker-aks.sh" -u "$username" -ai "$app_id" -ak "$app_key" -ti "$tenant_id" -si "$subscription_id" -rg "$resource_group" -vn "$vault_name" -acn "$aks_cluster_name" -arg "$aks_resource_group" -san "$storage_account_name" -sak "$storage_account_key" -r "$region" -al "$artifacts_location" -st "$artifacts_location_sas_token" -uspk "$use_ssh_public_key" 121 | fi 122 | -------------------------------------------------------------------------------- /quickstart_template/101-spinnaker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function print_usage() { 4 | cat <&2 21 | print_usage 22 | exit -1 23 | fi 24 | } 25 | 26 | function run_util_script() { 27 | local script_path="$1" 28 | shift 29 | curl --silent "${artifacts_location}${script_path}${artifacts_location_sas_token}" | sudo bash -s -- "$@" 30 | local return_value=$? 31 | if [ $return_value -ne 0 ]; then 32 | >&2 echo "Failed while executing script '$script_path'." 33 | exit $return_value 34 | fi 35 | } 36 | 37 | while [[ $# > 0 ]] 38 | do 39 | key="$1" 40 | shift 41 | case $key in 42 | --storage_account_name|-san) 43 | storage_account_name="$1";; 44 | --storage_account_key|-sak) 45 | storage_account_key="$1";; 46 | --username|-u) 47 | username="$1";; 48 | --artifacts_location|-al) 49 | artifacts_location="$1";; 50 | --sas_token|-st) 51 | artifacts_location_sas_token="$1";; 52 | --help|-help|-h) 53 | print_usage 54 | exit 13;; 55 | *) 56 | echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 57 | exit -1 58 | esac 59 | shift 60 | done 61 | 62 | throw_if_empty storage_account_name $storage_account_name 63 | throw_if_empty storage_account_key $storage_account_key 64 | throw_if_empty username $username 65 | 66 | run_util_script "spinnaker/install_halyard/install_halyard.sh" -san "$storage_account_name" -sak "$storage_account_key" -u "$username" 67 | 68 | sudo hal deploy apply 69 | 70 | # Wait for Spinnaker services to be up before returning 71 | timeout=180 72 | echo "while !(nc -z localhost 8084) || !(nc -z localhost 9000); do sleep 1; done" | timeout $timeout bash 73 | return_value=$? 74 | if [ $return_value -ne 0 ]; then 75 | >&2 echo "Failed to connect to Spinnaker within '$timeout' seconds." 76 | exit $return_value 77 | fi -------------------------------------------------------------------------------- /quickstart_template/201-jenkins-acr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | function print_usage() { 3 | cat <&2 25 | print_usage 26 | exit -1 27 | fi 28 | } 29 | 30 | function run_util_script() { 31 | local script_path="$1" 32 | shift 33 | curl --silent "${artifacts_location}${script_path}${artifacts_location_sas_token}" | sudo bash -s -- "$@" 34 | local return_value=$? 35 | if [ $return_value -ne 0 ]; then 36 | >&2 echo "Failed while executing script '$script_path'." 37 | exit $return_value 38 | fi 39 | } 40 | 41 | #defaults 42 | artifacts_location="https://raw.githubusercontent.com/Azure/azure-devops-utils/master/" 43 | while [[ $# > 0 ]] 44 | do 45 | key="$1" 46 | shift 47 | case $key in 48 | --vm_user_name|-u) 49 | vm_user_name="$1" 50 | shift 51 | ;; 52 | --git_url|-g) 53 | git_url="$1" 54 | shift 55 | ;; 56 | --registry|-r) 57 | registry="$1" 58 | shift 59 | ;; 60 | --registry_user_name|-ru) 61 | registry_user_name="$1" 62 | shift 63 | ;; 64 | --registry_password|-rp) 65 | registry_password="$1" 66 | shift 67 | ;; 68 | --repository|-rr) 69 | repository="$1" 70 | shift 71 | ;; 72 | --jenkins_fqdn|-jf) 73 | jenkins_fqdn="$1" 74 | shift 75 | ;; 76 | --artifacts_location|-al) 77 | artifacts_location="$1" 78 | shift 79 | ;; 80 | --sas_token|-st) 81 | artifacts_location_sas_token="$1" 82 | shift 83 | ;; 84 | --help|-help|-h) 85 | print_usage 86 | exit 13 87 | ;; 88 | *) 89 | echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 90 | exit -1 91 | esac 92 | done 93 | 94 | throw_if_empty --vm_user_name $vm_user_name 95 | throw_if_empty --git_url $git_url 96 | throw_if_empty --registry $registry 97 | throw_if_empty --registry_user_name $registry_user_name 98 | throw_if_empty --registry_password $registry_password 99 | throw_if_empty --jenkins_fqdn $jenkins_fqdn 100 | 101 | if [ -z "$repository" ]; then 102 | repository="${vm_user_name}/myfirstapp" 103 | fi 104 | 105 | #install jenkins 106 | run_util_script "jenkins/install_jenkins.sh" -jf "${jenkins_fqdn}" -al "${artifacts_location}" -st "${artifacts_location_sas_token}" 107 | 108 | #install git 109 | sudo apt-get install git --yes 110 | 111 | #install docker if not already installed 112 | if !(command -v docker >/dev/null); then 113 | sudo curl -sSL https://get.docker.com/ | sh 114 | fi 115 | 116 | #make sure jenkins has access to docker cli 117 | sudo gpasswd -a jenkins docker 118 | skill -KILL -u jenkins 119 | sudo service jenkins restart 120 | 121 | echo "Including the pipeline" 122 | run_util_script "jenkins/add-docker-build-job.sh" -j "http://localhost:8080/" -ju "admin" -g "${git_url}" -r "${registry}" -ru "${registry_user_name}" -rp "${registry_password}" -rr "$repository" -sps "* * * * *" -al "$artifacts_location" -st "$artifacts_location_sas_token" 123 | -------------------------------------------------------------------------------- /quickstart_template/301-jenkins-acr-spinnaker-k8s.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function print_usage() { 4 | cat <&2 33 | print_usage 34 | exit -1 35 | fi 36 | } 37 | 38 | function run_util_script() { 39 | local script_path="$1" 40 | shift 41 | curl --silent "${artifacts_location}${script_path}${artifacts_location_sas_token}" | sudo bash -s -- "$@" 42 | local return_value=$? 43 | if [ $return_value -ne 0 ]; then 44 | >&2 echo "Failed while executing script '$script_path'." 45 | exit $return_value 46 | fi 47 | } 48 | 49 | function add_empty_image_to_acr() { 50 | # Install docker if not already installed 51 | if !(command -v docker >/dev/null); then 52 | sudo curl -sSL https://get.docker.com/ | sh 53 | fi 54 | sudo gpasswd -a $user_name docker 55 | 56 | # Add (virtually) empty container to ACR to properly initialize Spinnaker. This fixes two bugs: 57 | # 1. The pipeline isn't triggered on the first push to the ACR (according to the source code, Igor "avoids publishing an event if this account has no indexed images (protects against a flushed redis)") 58 | # 2. Some dropdowns in the UI for the pipeline display a 'loading' symbol rather than the repository we configured 59 | temp_dir=$(mktemp -d) 60 | touch "$temp_dir/README" 61 | echo "This container is intentionally empty and only used as a placeholder." >"$temp_dir/README" 62 | touch "$temp_dir/Dockerfile" 63 | echo -e "FROM scratch\nADD . README" >"$temp_dir/Dockerfile" 64 | # We added the user to the docker group above, but that doesn't take effect until the next login so we still need to use sudo here 65 | sudo docker login "$azure_container_registry" -u "$app_id" -p "$app_key" 66 | sudo docker build $temp_dir --tag "$azure_container_registry/$docker_repository" 67 | sudo docker push "$azure_container_registry/$docker_repository" 68 | sudo docker rmi "$azure_container_registry/$docker_repository" 69 | sudo docker logout 70 | } 71 | 72 | function install_kubectl() { 73 | if !(command -v kubectl >/dev/null); then 74 | kubectl_file="/usr/local/bin/kubectl" 75 | sudo curl -L -s -o $kubectl_file https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl 76 | sudo chmod +x $kubectl_file 77 | fi 78 | } 79 | 80 | function install_az() { 81 | if !(command -v az >/dev/null); then 82 | sudo apt-get update && sudo apt-get install -y libssl-dev libffi-dev python-dev 83 | echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/azure-cli/ wheezy main" | sudo tee /etc/apt/sources.list.d/azure-cli.list 84 | sudo apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893 85 | sudo apt-get install -y apt-transport-https 86 | sudo apt-get -y update && sudo apt-get install -y azure-cli 87 | fi 88 | } 89 | 90 | function add_bash_login() { 91 | bash_login="/home/${user_name}/.bash_login" 92 | touch "$bash_login" 93 | cat < "$bash_login" 94 | ### Connect to Spinnaker ### 95 | echo "Connecting to Spinnaker..." 96 | hal deploy connect --no-validate &>/dev/null & 97 | 98 | # Wait for connection 99 | echo "while !(nc -z localhost 8084) || !(nc -z localhost 9000); do sleep 1; done" | timeout 20 bash 100 | 101 | if [ $? -ne 0 ]; then 102 | echo "Failed to connect to Spinnaker." 103 | else 104 | echo "Successfully connected to Spinnaker." 105 | echo "Enter 'Ctrl-C' in your terminal to exit the connection in the background." 106 | fi 107 | 108 | echo "Edit your ~/.bash_login file and remove the 'Connect to Spinnaker' section if you do not want to auto-connect on login." 109 | ### Connect to Spinnaker ### 110 | EOF 111 | } 112 | 113 | #Set defaults 114 | pipeline_port="8000" 115 | artifacts_location="https://raw.githubusercontent.com/Azure/azure-devops-utils/master/" 116 | docker_repository="${vm_user_name}/myfirstapp" 117 | 118 | while [[ $# > 0 ]] 119 | do 120 | key="$1" 121 | shift 122 | case $key in 123 | --app_id|-ai) 124 | app_id="$1" 125 | shift 126 | ;; 127 | --app_key|-ak) 128 | app_key="$1" 129 | shift 130 | ;; 131 | --subscription_id|-si) 132 | subscription_id="$1" 133 | shift 134 | ;; 135 | --tenant_id|-ti) 136 | tenant_id="$1" 137 | shift 138 | ;; 139 | --user_name|-un) 140 | user_name="$1" 141 | shift 142 | ;; 143 | --git_repository|-gr) 144 | git_repository="$1" 145 | shift 146 | ;; 147 | --resource_group|-rg) 148 | resource_group="$1" 149 | shift 150 | ;; 151 | --master_fqdn|-mf) 152 | master_fqdn="$1" 153 | shift 154 | ;; 155 | --storage_account_name|-san) 156 | storage_account_name="$1" 157 | shift 158 | ;; 159 | --storage_account_key|-sak) 160 | storage_account_key="$1" 161 | shift 162 | ;; 163 | --azure_container_registry|-acr) 164 | azure_container_registry="$1" 165 | shift 166 | ;; 167 | --jenkins_fqdn|-jf) 168 | jenkins_fqdn="$1" 169 | shift 170 | ;; 171 | --docker_repository|-dr) 172 | docker_repository="$1" 173 | shift 174 | ;; 175 | --pipeline_port|-pp) 176 | pipeline_port="$1" 177 | shift 178 | ;; 179 | --artifacts_location|-al) 180 | artifacts_location="$1" 181 | shift 182 | ;; 183 | --sas_token|-st) 184 | artifacts_location_sas_token="$1" 185 | shift 186 | ;; 187 | --help|-help|-h) 188 | print_usage 189 | exit 13 190 | ;; 191 | *) 192 | echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 193 | exit -1 194 | esac 195 | done 196 | 197 | throw_if_empty --app_id $app_id 198 | throw_if_empty --app_key $app_key 199 | throw_if_empty --subscription_id $subscription_id 200 | throw_if_empty --tenant_id $tenant_id 201 | throw_if_empty --user_name $user_name 202 | throw_if_empty --git_repository $git_repository 203 | throw_if_empty --resource_group $resource_group 204 | throw_if_empty --master_fqdn $master_fqdn 205 | throw_if_empty --storage_account_name $storage_account_name 206 | throw_if_empty --storage_account_key $storage_account_key 207 | throw_if_empty --azure_container_registry $azure_container_registry 208 | throw_if_empty --docker_repository $docker_repository 209 | throw_if_empty --pipeline_port $pipeline_port 210 | throw_if_empty --jenkins_fqdn $jenkins_fqdn 211 | 212 | add_empty_image_to_acr 213 | 214 | install_kubectl 215 | 216 | install_az 217 | 218 | run_util_script "spinnaker/install_halyard/install_halyard.sh" -san "$storage_account_name" -sak "$storage_account_key" -u "$user_name" 219 | 220 | # Copy kube config 221 | az login --service-principal -u "$app_id" -p "$app_key" --tenant "$tenant_id" 222 | az account set --subscription "$subscription_id" 223 | run_util_script "spinnaker/copy_kube_config/copy_kube_config.sh" -un "$user_name" -rg "$resource_group" -mf "$master_fqdn" 224 | az logout 225 | 226 | # Configure Spinnaker Docker Registry Accounts 227 | docker_hub_account="docker-hub-registry" 228 | hal config provider docker-registry account add $docker_hub_account \ 229 | --address "https://index.docker.io/" \ 230 | --repositories "library/nginx" "library/redis" "library/ubuntu" # Spinnaker can't get the entire catalog for a public registry, so we have to list a few repos 231 | acr_account="azure-container-registry" 232 | echo "$app_key" | hal config provider docker-registry account add $acr_account \ 233 | --address "https://$azure_container_registry/" \ 234 | --username "$app_id" \ 235 | --password 236 | hal config provider docker-registry enable 237 | 238 | # Configure Spinnaker Kubernetes Account 239 | kubeconfig_path="/home/${user_name}/.kube/config" 240 | my_kubernetes_account="my-kubernetes-account" 241 | hal config provider kubernetes account add $my_kubernetes_account \ 242 | --kubeconfig-file "$kubeconfig_path" \ 243 | --context $(kubectl config current-context --kubeconfig "$kubeconfig_path") \ 244 | --docker-registries "$docker_hub_account" "$acr_account" 245 | hal config provider kubernetes enable 246 | 247 | # Deploy Spinnaker to the Kubernetes cluster 248 | hal config deploy edit --account-name $my_kubernetes_account --type distributed 249 | hal deploy apply 250 | 251 | # Automatically connect to Spinnaker when logging in to DevOps VM 252 | add_bash_login 253 | 254 | # Add Kubernetes pipeline 255 | run_util_script "spinnaker/add_k8s_pipeline/add_k8s_pipeline.sh" \ 256 | -an "$acr_account" \ 257 | -rg "$azure_container_registry" \ 258 | -rp "$docker_repository" \ 259 | -p "$pipeline_port" \ 260 | -al "$artifacts_location" \ 261 | -st "$artifacts_location_sas_token" 262 | 263 | run_util_script "quickstart_template/201-jenkins-acr.sh" -u "$user_name" \ 264 | -g "$git_repository" \ 265 | -r "https://$azure_container_registry" \ 266 | -ru "$app_id" \ 267 | -rp "$app_key" \ 268 | -rr "$docker_repository" \ 269 | -jf "$jenkins_fqdn" \ 270 | -al "$artifacts_location" \ 271 | -st "$artifacts_location_sas_token" -------------------------------------------------------------------------------- /quickstart_template/301-jenkins-aptly-spinnaker-vmss.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function print_usage() { 4 | cat <&2 32 | print_usage 33 | exit -1 34 | fi 35 | } 36 | 37 | function run_util_script() { 38 | local script_path="$1" 39 | shift 40 | curl --silent "${artifacts_location}${script_path}${artifacts_location_sas_token}" | sudo bash -s -- "$@" 41 | local return_value=$? 42 | if [ $return_value -ne 0 ]; then 43 | >&2 echo "Failed while executing script '$script_path'." 44 | exit $return_value 45 | fi 46 | } 47 | 48 | # Set defaults 49 | region="westus" 50 | repository_name="hello-karyon-rxnetty" 51 | artifacts_location="https://raw.githubusercontent.com/Azure/azure-devops-utils/master/" 52 | artifacts_location_sas_token="" 53 | 54 | while [[ $# > 0 ]] 55 | do 56 | key="$1" 57 | shift 58 | case $key in 59 | --app_id|-ai) 60 | app_id="$1";; 61 | --app_key|-ak) 62 | app_key="$1";; 63 | --jenkins_username|-ju) 64 | jenkins_username="$1";; 65 | --jenkins_password|-jp) 66 | jenkins_password="$1";; 67 | --tenant_id|-ti) 68 | tenant_id="$1";; 69 | --subscription_id|-si) 70 | subscription_id="$1";; 71 | --resource_group|-rg) 72 | resource_group="$1";; 73 | --vault_name|-vn) 74 | vault_name="$1";; 75 | --storage_account_name|-san) 76 | storage_account_name="$1";; 77 | --storage_account_key|-sak) 78 | storage_account_key="$1";; 79 | --region|-r) 80 | region="$1";; 81 | --vm_fqdn|-vf) 82 | vm_fqdn="$1";; 83 | --use_ssh_public_key|-uspk) 84 | use_ssh_public_key="$1";; 85 | --artifacts_location|-al) 86 | artifacts_location="$1";; 87 | --sas_token|-st) 88 | artifacts_location_sas_token="$1";; 89 | --help|-help|-h) 90 | print_usage 91 | exit 13;; 92 | *) 93 | echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 94 | exit -1 95 | esac 96 | shift 97 | done 98 | 99 | throw_if_empty app_id $app_id 100 | throw_if_empty app_key $app_key 101 | throw_if_empty jenkins_username $jenkins_username 102 | throw_if_empty jenkins_password $jenkins_password 103 | throw_if_empty tenant_id $tenant_id 104 | throw_if_empty subscription_id $subscription_id 105 | throw_if_empty resource_group $resource_group 106 | throw_if_empty vault_name $vault_name 107 | throw_if_empty storage_account_name $storage_account_name 108 | throw_if_empty storage_account_key $storage_account_key 109 | throw_if_empty vm_fqdn $vm_fqdn 110 | throw_if_empty use_ssh_public_key $use_ssh_public_key 111 | throw_if_empty region $region 112 | 113 | default_hal_config="/home/$jenkins_username/.hal/default" 114 | 115 | run_util_script "spinnaker/install_halyard/install_halyard.sh" -san "$storage_account_name" -sak "$storage_account_key" -u "$jenkins_username" 116 | # Change front50 port so it doesn't conflict with Jenkins 117 | front50_settings="$default_hal_config/service-settings/front50.yml" 118 | sudo -u $jenkins_username mkdir -p $(dirname "$front50_settings") 119 | sudo -u $jenkins_username touch "$front50_settings" 120 | echo "port: $front50_port" > "$front50_settings" 121 | 122 | # Configure Azure provider for Spinnaker 123 | echo "$app_key" | hal config provider azure account add my-azure-account \ 124 | --client-id "$app_id" \ 125 | --tenant-id "$tenant_id" \ 126 | --subscription-id "$subscription_id" \ 127 | --default-key-vault "$vault_name" \ 128 | --default-resource-group "$resource_group" \ 129 | --packer-resource-group "$resource_group" \ 130 | --useSshPublicKey "$use_ssh_public_key" \ 131 | --app-key 132 | 133 | #change region if region not in eastus or westus 134 | if [ "$region" != eastus ] && [ "$region" != westus ]; then 135 | hal config provider azure account edit my-azure-account \ 136 | --regions "eastus","westus","$region" 137 | fi 138 | hal config provider azure enable 139 | 140 | # Configure Rosco (these params are not supported by Halyard yet) 141 | rosco_config="$default_hal_config/profiles/rosco-local.yml" 142 | sudo -u $jenkins_username mkdir -p $(dirname "$rosco_config") 143 | sudo -u $jenkins_username touch "$rosco_config" 144 | cat < "$rosco_config" 145 | debianRepository: http://ppa.launchpad.net/openjdk-r/ppa/ubuntu trusty main;http://$vm_fqdn:9999 trusty main 146 | defaultCloudProviderType: azure 147 | EOF 148 | 149 | # Configure Jenkins for Spinnaker 150 | echo "$jenkins_password" | hal config ci jenkins master add Jenkins \ 151 | --address "http://localhost:8082" \ 152 | --username "$jenkins_username" \ 153 | --password 154 | 155 | hal config ci jenkins enable 156 | 157 | run_util_script "jenkins/install_jenkins.sh" -jf "${vm_fqdn}" -al "${artifacts_location}" -st "${artifacts_location_sas_token}" 158 | 159 | run_util_script "jenkins/init-aptly-repo.sh" -vf "${vm_fqdn}" -rn "${repository_name}" 160 | 161 | run_util_script "jenkins/add-aptly-build-job.sh" -al "${artifacts_location}" -st "${artifacts_location_sas_token}" 162 | 163 | echo "Setting up initial user..." 164 | 165 | # Using single quote for username and password here to avoid dollar sign being recognized as start of variable 166 | echo "jenkins.model.Jenkins.instance.securityRealm.createAccount('$jenkins_username', '$jenkins_password')" > addUser.groovy 167 | 168 | run_util_script "jenkins/run-cli-command.sh" -cif "addUser.groovy" -c "groovy =" 169 | 170 | rm "addUser.groovy" 171 | 172 | # Change the Jenkins port in order not to conflict with the Spinnaker front50 port 173 | port=8082 174 | 175 | sed -i -e "s/\(HTTP_PORT=\).*/\1$port/" /etc/default/jenkins 176 | 177 | service jenkins restart 178 | 179 | sudo hal deploy apply 180 | 181 | # If redis is not started, start the redis-server 182 | netstat -tln | grep ":6379 " 183 | 184 | if [ $? -eq 1 ] 185 | then 186 | echo "Redis is not started. Start the redis-server." 187 | sudo redis-server /etc/redis/redis.conf 188 | sleep 5 189 | fi 190 | 191 | # Double check and log if redis is still not running 192 | netstat -tln | grep ":6379 " 193 | 194 | if [ $? -eq 1 ] 195 | then 196 | echo "Redis failed to start for second time. Related spinnaker micro-services might fail to start." 197 | fi 198 | 199 | netstat -tln | grep ":8083 " 200 | if [ $? -eq 1 ] 201 | then 202 | sudo systemctl restart orca.service 203 | fi 204 | 205 | netstat -tln | grep ":8080 " 206 | if [ $? -eq 1 ] 207 | then 208 | sudo systemctl restart front50.service 209 | fi 210 | 211 | netstat -tln | grep ":8084 " 212 | if [ $? -eq 1 ] 213 | then 214 | sudo systemctl restart gate.service 215 | fi 216 | 217 | # Wait for Spinnaker services to be up before returning 218 | timeout=180 219 | echo "while !(nc -z localhost 8084) || !(nc -z localhost 9000); do sleep 1; done" | timeout $timeout bash 220 | return_value=$? 221 | if [ $return_value -ne 0 ]; then 222 | >&2 echo "Failed to connect to Spinnaker within '$timeout' seconds." 223 | exit $return_value 224 | fi -------------------------------------------------------------------------------- /quickstart_template/301-jenkins-k8s-blue-green.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function print_usage() { 4 | cat <&2 26 | print_usage 27 | exit -1 28 | fi 29 | } 30 | 31 | function run_util_script() { 32 | local script_path="$1" 33 | shift 34 | curl --silent "${artifacts_location}${script_path}${artifacts_location_sas_token}" | sudo bash -s -- "$@" 35 | local return_value=$? 36 | if [ $return_value -ne 0 ]; then 37 | >&2 echo "Failed while executing script '$script_path'." 38 | exit $return_value 39 | fi 40 | } 41 | 42 | function install_kubectl() { 43 | if !(command -v kubectl >/dev/null); then 44 | kubectl_file="/usr/local/bin/kubectl" 45 | sudo curl -L -s -o $kubectl_file https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl 46 | sudo chmod +x $kubectl_file 47 | fi 48 | } 49 | 50 | function install_az() { 51 | if !(command -v az >/dev/null); then 52 | sudo apt-get update && sudo apt-get install -y libssl-dev libffi-dev python-dev 53 | echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/azure-cli/ wheezy main" | sudo tee /etc/apt/sources.list.d/azure-cli.list 54 | sudo apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893 55 | sudo apt-get install -y apt-transport-https 56 | sudo apt-get -y update && sudo apt-get install -y azure-cli 57 | fi 58 | } 59 | 60 | artifacts_location="https://raw.githubusercontent.com/Azure/azure-devops-utils/master/" 61 | 62 | while [[ $# > 0 ]] 63 | do 64 | key="$1" 65 | shift 66 | case "$key" in 67 | --app_id|-ai) 68 | app_id="$1" 69 | shift 70 | ;; 71 | --app_key|-ak) 72 | app_key="$1" 73 | shift 74 | ;; 75 | --subscription_id|-si) 76 | subscription_id="$1" 77 | shift 78 | ;; 79 | --tenant_id|-ti) 80 | tenant_id="$1" 81 | shift 82 | ;; 83 | --resource_group|-rg) 84 | resource_group="$1" 85 | shift 86 | ;; 87 | --aks_name|-an) 88 | aks_name="$1" 89 | shift 90 | ;; 91 | --jenkins_fqdn|-jf) 92 | jenkins_fqdn="$1" 93 | shift 94 | ;; 95 | --artifacts_location|-al) 96 | artifacts_location="$1" 97 | shift 98 | ;; 99 | --sas_token|-st) 100 | artifacts_location_sas_token="$1" 101 | shift 102 | ;; 103 | --help|-help|-h) 104 | print_usage 105 | exit 13 106 | ;; 107 | *) 108 | echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 109 | exit -1 110 | esac 111 | done 112 | 113 | throw_if_empty --app_id "$app_id" 114 | throw_if_empty --app_key "$app_key" 115 | throw_if_empty --subscription_id "$subscription_id" 116 | throw_if_empty --tenant_id "$tenant_id" 117 | throw_if_empty --resource_group "$resource_group" 118 | throw_if_empty --aks_name "$aks_name" 119 | throw_if_empty --jenkins_fqdn "$jenkins_fqdn" 120 | 121 | install_kubectl 122 | 123 | install_az 124 | 125 | sudo apt-get install --yes jq 126 | 127 | #install jenkins 128 | #install jenkins 129 | run_util_script "jenkins/install_jenkins.sh" \ 130 | --jenkins_release_type verified \ 131 | --jenkins_version_location "${artifacts_location}jenkins/blue-green/verified-jenkins-version${artifacts_location_sas_token}" \ 132 | --jenkins_fqdn "${jenkins_fqdn}" \ 133 | --artifacts_location "${artifacts_location}" \ 134 | --sas_token "${artifacts_location_sas_token}" 135 | 136 | run_util_script "jenkins/blue-green/bootstrap-k8s-blue-green.sh" \ 137 | --resource_group "$resource_group" \ 138 | --aks_name "$aks_name" \ 139 | --sp_subscription_id "$subscription_id" \ 140 | --sp_client_id "$app_id" \ 141 | --sp_client_password "$app_key" \ 142 | --sp_tenant_id "$tenant_id" \ 143 | --artifacts_location "$artifacts_location" \ 144 | --sas_token "$artifacts_location_sas_token" 145 | 146 | run_util_script "jenkins/blue-green/add-blue-green-job.sh" \ 147 | -j "http://localhost:8080/" \ 148 | -ju "admin" \ 149 | --aks_resource_group "$resource_group" \ 150 | --aks_name "$aks_name" \ 151 | --sp_subscription_id "$subscription_id" \ 152 | --sp_client_id "$app_id" \ 153 | --sp_client_password "$app_key" \ 154 | --sp_tenant_id "$tenant_id" \ 155 | --artifacts_location "$artifacts_location" \ 156 | --sas_token "$artifacts_location_sas_token" 157 | 158 | rm -f "$temp_key_path" 159 | rm -f "$temp_pub_key" 160 | -------------------------------------------------------------------------------- /solution_template/jenkins/createUiDefinition.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", 3 | "handler": "Microsoft.Compute.MultiVm", 4 | "version": "0.1.2-preview", 5 | "parameters": { 6 | "basics": [ 7 | { 8 | "name": "vmName", 9 | "type": "Microsoft.Common.TextBox", 10 | "label": "Name", 11 | "defaultValue": "Jenkins", 12 | "toolTip": "", 13 | "constraints": { 14 | "required": true, 15 | "regex": "^[a-z0-9A-Z]{1,54}$", 16 | "validationMessage": "Only alphanumeric characters are allowed, and the value must be 1-54 characters long." 17 | }, 18 | "visible": true 19 | }, 20 | { 21 | "name": "userName", 22 | "type": "Microsoft.Compute.UserNameTextBox", 23 | "label": "User name", 24 | "defaultValue": "", 25 | "toolTip": "", 26 | "constraints": { 27 | "required": true 28 | }, 29 | "osPlatform": "Linux", 30 | "visible": true 31 | }, 32 | { 33 | "name": "authType", 34 | "type": "Microsoft.Compute.CredentialsCombo", 35 | "label": { 36 | "authenticationType": "Authentication type", 37 | "password": "Password", 38 | "confirmPassword": "Confirm password", 39 | "sshPublicKey": "SSH public key" 40 | }, 41 | "toolTip": { 42 | "authenticationType": "", 43 | "password": "", 44 | "sshPublicKey": "" 45 | }, 46 | "constraints": { 47 | "required": true, 48 | "customPasswordRegex": "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d@#\\$%\\^&\\*-_!+=\\[\\]\\{\\}\\|\\\\:',\\.\\?\\/`~\"\\(\\);]{8,}$", 49 | "customValidationMessage": "The password must contain at least 8 characters, with at least 1 letter and 1 number. Alphanumerical (@#$%^&*-_!+=[]{}|\\:',.?/`~\"();)and special characters are allowed" 50 | }, 51 | "options": { 52 | "hideConfirmation": false, 53 | "hidePassword": false 54 | }, 55 | "osPlatform": "Linux", 56 | "visible": true 57 | } 58 | ], 59 | "steps": [ 60 | { 61 | "name": "firstStep", 62 | "label": "Additional Settings", 63 | "bladeTitle": "Additional Settings", 64 | "subLabel": { 65 | "preValidation": "", 66 | "postValidation": "Done" 67 | }, 68 | "elements": [ 69 | { 70 | "name": "vmSize", 71 | "type": "Microsoft.Compute.SizeSelector", 72 | "label": "Size", 73 | "toolTip": "", 74 | "recommendedSizes": [ 75 | "Standard_DS2_v2", 76 | "Standard_DS1_v2", 77 | "Standard_DS11_v2" 78 | ], 79 | "constraints": { 80 | "allowedSizes": [ 81 | "Standard_DS1_v2", 82 | "Standard_DS2_v2", 83 | "Standard_DS3_v2", 84 | "Standard_DS4_v2", 85 | "Standard_DS5_v2", 86 | "Standard_DS11_v2" 87 | ] 88 | }, 89 | "osPlatform": "Linux", 90 | "count": 1, 91 | "visible": true 92 | }, 93 | { 94 | "name": "vmDiskType", 95 | "type": "Microsoft.Common.OptionsGroup", 96 | "label": "VM disk type", 97 | "defaultValue": "SSD", 98 | "toolTip": "Premium disks (SSD) are backed by solid state drives and offer consistent, low-latency performance. They provide the best balance between price and performance, and are ideal for I/O-intensive applications and production workloads. Standard disks (HDD) are backed by magnetic drives and are preferable for applications where data is accessed infrequently.", 99 | "constraints": { 100 | "allowedValues": [ 101 | { 102 | "label": "SSD", 103 | "value": "Premium_LRS" 104 | }, 105 | { 106 | "label": "HDD", 107 | "value": "Standard_LRS" 108 | } 109 | ] 110 | }, 111 | "visible": true 112 | }, 113 | { 114 | "name": "publicIP", 115 | "type": "Microsoft.Network.PublicIpAddressCombo", 116 | "label": { 117 | "publicIpAddress": "Public IP address", 118 | "domainNameLabel": "Domain name label" 119 | }, 120 | "toolTip": { 121 | "publicIpAddress": "", 122 | "domainNameLabel": "" 123 | }, 124 | "defaultValue": { 125 | "publicIpAddressName": "JenkinsIP", 126 | "domainNameLabel": "" 127 | }, 128 | "constraints": { 129 | "required": { 130 | "domainNameLabel": true 131 | } 132 | }, 133 | "options": { 134 | "hideNone": true, 135 | "hideDomainNameLabel": false, 136 | "hideExisting": false 137 | }, 138 | "visible": true 139 | }, 140 | { 141 | "name": "jenkinsReleaseType", 142 | "type": "Microsoft.Common.DropDown", 143 | "label": "Jenkins release type", 144 | "defaultValue": "LTS", 145 | "toolTip": "", 146 | "constraints": { 147 | "allowedValues": [ 148 | { 149 | "label": "LTS", 150 | "value": "LTS" 151 | }, 152 | { 153 | "label": "Weekly Build", 154 | "value": "weekly" 155 | }, 156 | { 157 | "label": "Azure Verified", 158 | "value": "verified" 159 | } 160 | ] 161 | }, 162 | "visible": true 163 | } 164 | ] 165 | }, 166 | { 167 | "name": "secondStep", 168 | "label": "Integration Settings", 169 | "bladeTitle": "Jenkins Integration Settings", 170 | "subLabel": { 171 | "preValidation": "", 172 | "postValidation": "Done" 173 | }, 174 | "elements": [ 175 | { 176 | "name": "sptype", 177 | "type": "Microsoft.Common.DropDown", 178 | "label": "Service Pricipal Integration", 179 | "defaultValue": "Auto(MSI)", 180 | "toolTip": "The service pricipal will be added into Jenkins as a credential for authenticating with Azure. 'Auto' means that the principal will be created by MSI (Managed Service Identity). 'Manual' means the principal should be created by user and be filled in below.", 181 | "constraints": { 182 | "allowedValues": [ 183 | { 184 | "label": "Auto(MSI)", 185 | "value": "msi" 186 | }, 187 | { 188 | "label": "Manual", 189 | "value": "manual" 190 | } 191 | ] 192 | }, 193 | "visible": true 194 | }, 195 | { 196 | "name": "spSection", 197 | "type": "Microsoft.Common.Section", 198 | "label": "Service Principal", 199 | "elements": [ 200 | { 201 | "name": "spid", 202 | "type": "Microsoft.Common.TextBox", 203 | "label": "Application ID", 204 | "defaultValue": "", 205 | "toolTip": "", 206 | "visible": true 207 | }, 208 | { 209 | "name": "spsecret", 210 | "type": "Microsoft.Common.PasswordBox", 211 | "label": { 212 | "password":"Secret", 213 | "confirmPassword": "" 214 | }, 215 | "toolTip": "", 216 | "options": { 217 | "hideConfirmation": true 218 | }, 219 | "visible": true 220 | } 221 | ], 222 | "visible": "[equals(steps('secondStep').sptype,'manual')]" 223 | }, 224 | { 225 | "name": "enableCloudAgents", 226 | "type": "Microsoft.Common.OptionsGroup", 227 | "label": "Enable Cloud Agents", 228 | "defaultValue": "VM", 229 | "toolTip": "Add a default cloud template for agents. ACI: Azure Container Instance, VM: virtual machine.", 230 | "constraints": { 231 | "allowedValues": [ 232 | { 233 | "label": "No", 234 | "value": "no" 235 | }, 236 | { 237 | "label": "ACI", 238 | "value": "aci" 239 | }, 240 | { 241 | "label": "VM", 242 | "value": "vm" 243 | } 244 | ] 245 | }, 246 | "visible": true 247 | } 248 | ] 249 | } 250 | ], 251 | "outputs": { 252 | "adminPassword": "[basics('authType').password]", 253 | "adminSSHPublicKey": "[basics('authType').sshPublicKey]", 254 | "adminUserName": "[basics('userName')]", 255 | "authenticationType": "[basics('authType').authenticationType]", 256 | "dnsPrefix": "[steps('firstStep').publicIP.domainNameLabel]", 257 | "publicIPName": "[steps('firstStep').publicIP.name]", 258 | "publicIPNewOrExisting": "[steps('firstStep').publicIP.newOrExistingOrNone]", 259 | "publicIPResourceGroup": "[steps('firstStep').publicIP.resourceGroup]", 260 | "storageAccountType": "[steps('firstStep').vmDiskType]", 261 | "vmName": "[basics('vmName')]", 262 | "vmSize": "[steps('firstStep').vmSize]", 263 | "location": "[location()]", 264 | "jenkinsReleaseType": "[steps('firstStep').jenkinsReleaseType]", 265 | "spType": "[steps('secondStep').sptype]", 266 | "spId": "[steps('secondStep').spSection.spid]", 267 | "spSecret": "[steps('secondStep').spSection.spsecret]", 268 | "enableCloudAgents": "[steps('secondStep').enableCloudAgents]" 269 | } 270 | } 271 | } -------------------------------------------------------------------------------- /solution_template/nested/VM-new-password.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "outputs": { 5 | "identity": { 6 | "type": "object", 7 | "value": "[reference(concat(resourceId('Microsoft.Compute/virtualMachines', parameters('vmName')), '/providers/Microsoft.ManagedIdentity/Identities/default'), '2015-08-31-PREVIEW')]" 8 | } 9 | }, 10 | "parameters": { 11 | "adminPassword": { 12 | "type": "securestring" 13 | }, 14 | "adminSSHPublicKey": { 15 | "type": "securestring" 16 | }, 17 | "adminUserName": { 18 | "type": "string" 19 | }, 20 | "networkInterfaceId": { 21 | "type": "string" 22 | }, 23 | "storageAccountType": { 24 | "type": "string" 25 | }, 26 | "ubuntuSku": { 27 | "type": "string" 28 | }, 29 | "vmLocation": { 30 | "type": "string" 31 | }, 32 | "vmName": { 33 | "type": "string" 34 | }, 35 | "vmSize": { 36 | "type": "string" 37 | } 38 | }, 39 | "resources": [ 40 | { 41 | "apiVersion": "2017-03-30", 42 | "dependsOn": [], 43 | "location": "[parameters('vmLocation')]", 44 | "name": "[parameters('vmName')]", 45 | "identity": { 46 | "Type": "SystemAssigned" 47 | }, 48 | "properties": { 49 | "hardwareProfile": { 50 | "vmSize": "[parameters('vmSize')]" 51 | }, 52 | "networkProfile": { 53 | "networkInterfaces": [ 54 | { 55 | "id": "[parameters('networkInterfaceId')]" 56 | } 57 | ] 58 | }, 59 | "osProfile": { 60 | "adminPassword": "[parameters('adminPassword')]", 61 | "adminUsername": "[parameters('adminUserName')]", 62 | "computerName": "[parameters('vmName')]" 63 | }, 64 | "storageProfile": { 65 | "imageReference": { 66 | "offer": "UbuntuServer", 67 | "publisher": "Canonical", 68 | "sku": "[parameters('ubuntuSku')]", 69 | "version": "latest" 70 | }, 71 | "osDisk": { 72 | "caching": "ReadWrite", 73 | "createOption": "FromImage", 74 | "name": "osDisk", 75 | "managedDisk":{ 76 | "storageAccountType": "[parameters('storageAccountType')]" 77 | } 78 | } 79 | } 80 | }, 81 | "resources": [], 82 | "tags": { 83 | "displayName": "[parameters('vmName')]" 84 | }, 85 | "type": "Microsoft.Compute/virtualMachines" 86 | } 87 | ], 88 | "variables": {} 89 | } -------------------------------------------------------------------------------- /solution_template/nested/VM-new-sshPublicKey.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "outputs": { 5 | "identity": { 6 | "type": "object", 7 | "value": "[reference(concat(resourceId('Microsoft.Compute/virtualMachines', parameters('vmName')), '/providers/Microsoft.ManagedIdentity/Identities/default'), '2015-08-31-PREVIEW')]" 8 | } 9 | }, 10 | "parameters": { 11 | "adminPassword": { 12 | "type": "securestring" 13 | }, 14 | "adminSSHPublicKey": { 15 | "type": "securestring" 16 | }, 17 | "adminUserName": { 18 | "type": "string" 19 | }, 20 | "networkInterfaceId": { 21 | "type": "string" 22 | }, 23 | "storageAccountType": { 24 | "type": "string" 25 | }, 26 | "ubuntuSku": { 27 | "type": "string" 28 | }, 29 | "vmLocation": { 30 | "type": "string" 31 | }, 32 | "vmName": { 33 | "type": "string" 34 | }, 35 | "vmSize": { 36 | "type": "string" 37 | } 38 | }, 39 | "resources": [ 40 | { 41 | "apiVersion": "2017-03-30", 42 | "dependsOn": [], 43 | "location": "[parameters('vmLocation')]", 44 | "identity": { 45 | "Type": "SystemAssigned" 46 | }, 47 | "name": "[parameters('vmName')]", 48 | "properties": { 49 | "hardwareProfile": { 50 | "vmSize": "[parameters('vmSize')]" 51 | }, 52 | "networkProfile": { 53 | "networkInterfaces": [ 54 | { 55 | "id": "[parameters('networkInterfaceId')]" 56 | } 57 | ] 58 | }, 59 | "osProfile": { 60 | "adminUsername": "[parameters('adminUserName')]", 61 | "computerName": "[parameters('vmName')]", 62 | "linuxConfiguration": { 63 | "disablePasswordAuthentication": "true", 64 | "ssh": { 65 | "publicKeys": [ 66 | { 67 | "keyData": "[parameters('adminSSHPublicKey')]", 68 | "path": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]" 69 | } 70 | ] 71 | } 72 | } 73 | }, 74 | "storageProfile": { 75 | "imageReference": { 76 | "offer": "UbuntuServer", 77 | "publisher": "Canonical", 78 | "sku": "[parameters('ubuntuSku')]", 79 | "version": "latest" 80 | }, 81 | "osDisk": { 82 | "caching": "ReadWrite", 83 | "createOption": "FromImage", 84 | "name": "osDisk", 85 | "managedDisk":{ 86 | "storageAccountType": "[parameters('storageAccountType')]" 87 | } 88 | } 89 | } 90 | }, 91 | "resources": [], 92 | "tags": { 93 | "displayName": "[parameters('vmName')]" 94 | }, 95 | "type": "Microsoft.Compute/virtualMachines" 96 | } 97 | ], 98 | "variables": {} 99 | } -------------------------------------------------------------------------------- /solution_template/nested/VM-password.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "outputs": {}, 5 | "parameters": { 6 | "adminPassword": { 7 | "type": "securestring" 8 | }, 9 | "adminSSHPublicKey": { 10 | "type": "securestring" 11 | }, 12 | "adminUserName": { 13 | "type": "string" 14 | }, 15 | "networkInterfaceId": { 16 | "type": "string" 17 | }, 18 | "osDiskVHDUri": { 19 | "type": "string" 20 | }, 21 | "ubuntuSku": { 22 | "type": "string" 23 | }, 24 | "vmLocation": { 25 | "type": "string" 26 | }, 27 | "vmName": { 28 | "type": "string" 29 | }, 30 | "vmSize": { 31 | "type": "string" 32 | } 33 | }, 34 | "resources": [ 35 | { 36 | "apiVersion": "2016-03-30", 37 | "dependsOn": [], 38 | "location": "[parameters('vmLocation')]", 39 | "name": "[parameters('vmName')]", 40 | "properties": { 41 | "hardwareProfile": { 42 | "vmSize": "[parameters('vmSize')]" 43 | }, 44 | "networkProfile": { 45 | "networkInterfaces": [ 46 | { 47 | "id": "[parameters('networkInterfaceId')]" 48 | } 49 | ] 50 | }, 51 | "osProfile": { 52 | "adminPassword": "[parameters('adminPassword')]", 53 | "adminUsername": "[parameters('adminUserName')]", 54 | "computerName": "[parameters('vmName')]" 55 | }, 56 | "storageProfile": { 57 | "imageReference": { 58 | "offer": "UbuntuServer", 59 | "publisher": "Canonical", 60 | "sku": "[parameters('ubuntuSku')]", 61 | "version": "latest" 62 | }, 63 | "osDisk": { 64 | "caching": "ReadWrite", 65 | "createOption": "FromImage", 66 | "name": "osDisk", 67 | "vhd": { 68 | "uri": "[parameters('osDiskVHDUri')]" 69 | } 70 | } 71 | } 72 | }, 73 | "resources": [], 74 | "tags": { 75 | "displayName": "[parameters('vmName')]" 76 | }, 77 | "type": "Microsoft.Compute/virtualMachines" 78 | } 79 | ], 80 | "variables": {} 81 | } -------------------------------------------------------------------------------- /solution_template/nested/VM-sshPublicKey.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "outputs": {}, 5 | "parameters": { 6 | "adminPassword": { 7 | "type": "securestring" 8 | }, 9 | "adminSSHPublicKey": { 10 | "type": "securestring" 11 | }, 12 | "adminUserName": { 13 | "type": "string" 14 | }, 15 | "networkInterfaceId": { 16 | "type": "string" 17 | }, 18 | "osDiskVHDUri": { 19 | "type": "string" 20 | }, 21 | "ubuntuSku": { 22 | "type": "string" 23 | }, 24 | "vmLocation": { 25 | "type": "string" 26 | }, 27 | "vmName": { 28 | "type": "string" 29 | }, 30 | "vmSize": { 31 | "type": "string" 32 | } 33 | }, 34 | "resources": [ 35 | { 36 | "apiVersion": "2016-03-30", 37 | "dependsOn": [], 38 | "location": "[parameters('vmLocation')]", 39 | "name": "[parameters('vmName')]", 40 | "properties": { 41 | "hardwareProfile": { 42 | "vmSize": "[parameters('vmSize')]" 43 | }, 44 | "networkProfile": { 45 | "networkInterfaces": [ 46 | { 47 | "id": "[parameters('networkInterfaceId')]" 48 | } 49 | ] 50 | }, 51 | "osProfile": { 52 | "adminUsername": "[parameters('adminUserName')]", 53 | "computerName": "[parameters('vmName')]", 54 | "linuxConfiguration": { 55 | "disablePasswordAuthentication": "true", 56 | "ssh": { 57 | "publicKeys": [ 58 | { 59 | "keyData": "[parameters('adminSSHPublicKey')]", 60 | "path": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]" 61 | } 62 | ] 63 | } 64 | } 65 | }, 66 | "storageProfile": { 67 | "imageReference": { 68 | "offer": "UbuntuServer", 69 | "publisher": "Canonical", 70 | "sku": "[parameters('ubuntuSku')]", 71 | "version": "latest" 72 | }, 73 | "osDisk": { 74 | "caching": "ReadWrite", 75 | "createOption": "FromImage", 76 | "name": "osDisk", 77 | "vhd": { 78 | "uri": "[parameters('osDiskVHDUri')]" 79 | } 80 | } 81 | } 82 | }, 83 | "resources": [], 84 | "tags": { 85 | "displayName": "[parameters('vmName')]" 86 | }, 87 | "type": "Microsoft.Compute/virtualMachines" 88 | } 89 | ], 90 | "variables": {} 91 | } -------------------------------------------------------------------------------- /solution_template/nested/publicIP-existing.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "outputs": { 5 | "fqdn": { 6 | "type": "string", 7 | "value": "[reference(resourceId(parameters('publicIPResourceGroup'),'Microsoft.Network/publicIPAddresses',parameters('publicIPName')),'2016-03-30').dnsSettings.fqdn]" 8 | } 9 | }, 10 | "parameters": { 11 | "dnsPrefix": { 12 | "type": "string" 13 | }, 14 | "publicIPLocation": { 15 | "type": "string" 16 | }, 17 | "publicIPName": { 18 | "type": "string" 19 | }, 20 | "publicIPResourceGroup": { 21 | "type": "string" 22 | } 23 | }, 24 | "resources": [], 25 | "variables": {} 26 | } -------------------------------------------------------------------------------- /solution_template/nested/publicIP-new.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "outputs": { 5 | "fqdn": { 6 | "type": "string", 7 | "value": "[reference(resourceId('Microsoft.Network/publicIPAddresses',parameters('publicIPName')),'2016-03-30').dnsSettings.fqdn]" 8 | } 9 | }, 10 | "parameters": { 11 | "dnsPrefix": { 12 | "type": "string" 13 | }, 14 | "publicIPLocation": { 15 | "type": "string" 16 | }, 17 | "publicIPName": { 18 | "type": "string" 19 | }, 20 | "publicIPResourceGroup": { 21 | "type": "string" 22 | } 23 | }, 24 | "resources": [ 25 | { 26 | "apiVersion": "2016-03-30", 27 | "location": "[parameters('publicIPLocation')]", 28 | "name": "[parameters('publicIPName')]", 29 | "properties": { 30 | "dnsSettings": { 31 | "domainNameLabel": "[parameters('dnsPrefix')]" 32 | }, 33 | "publicIPAllocationMethod": "Dynamic" 34 | }, 35 | "type": "Microsoft.Network/publicIPAddresses" 36 | } 37 | ], 38 | "variables": {} 39 | } -------------------------------------------------------------------------------- /solution_template/nested/spinnaker-VM-Init.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "outputs": {}, 5 | "parameters": { 6 | "location": { 7 | "type": "string" 8 | }, 9 | "vmName": { 10 | "type": "string" 11 | }, 12 | "_extensionScript": { 13 | "type": "string" 14 | }, 15 | "storageAccountName": { 16 | "type": "string" 17 | }, 18 | "storageAccountResourceGroup": { 19 | "type": "string" 20 | }, 21 | "adminUsername": { 22 | "type": "string" 23 | }, 24 | "_artifactsLocation": { 25 | "type": "string" 26 | }, 27 | "_artifactsLocationSasToken": { 28 | "type": "string" 29 | } 30 | }, 31 | "resources": [ 32 | { 33 | "apiVersion": "2015-06-15", 34 | "dependsOn": [], 35 | "location": "[parameters('location')]", 36 | "name": "[concat(parameters('vmName'), '/Init')]", 37 | "properties": { 38 | "autoUpgradeMinorVersion": true, 39 | "protectedSettings": { 40 | "commandToExecute": "[concat('./', parameters('_extensionScript'), ' -san \"', parameters('storageAccountName'), '\" -sak \"', listKeys(resourceId(parameters('storageAccountResourceGroup'), 'Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2016-01-01').keys[0].value, '\" -u \"', parameters('adminUsername'), '\" -al \"', parameters('_artifactsLocation'), '\" -st \"', parameters('_artifactsLocationSasToken'), '\"')]" 41 | }, 42 | "publisher": "Microsoft.Azure.Extensions", 43 | "settings": { 44 | "fileUris": [ 45 | "[concat(parameters('_artifactsLocation'), 'quickstart_template/', parameters('_extensionScript'), parameters('_artifactsLocationSasToken'))]" 46 | ] 47 | }, 48 | "type": "CustomScript", 49 | "typeHandlerVersion": "2.0" 50 | }, 51 | "type": "Microsoft.Compute/virtualMachines/extensions" 52 | } 53 | ], 54 | "variables": {} 55 | } -------------------------------------------------------------------------------- /solution_template/nested/storageAccount-existing.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "outputs": { 5 | "primaryEndpointBlob": { 6 | "type": "string", 7 | "value": "[reference(resourceId(parameters('storageAccountResourceGroup'),'Microsoft.Storage/storageAccounts',parameters('storageAccountName')),'2016-01-01').primaryEndpoints.blob]" 8 | } 9 | }, 10 | "parameters": { 11 | "storageAccountLocation": { 12 | "type": "string" 13 | }, 14 | "storageAccountName": { 15 | "type": "string" 16 | }, 17 | "storageAccountType": { 18 | "type": "string" 19 | }, 20 | "storageAccountResourceGroup": { 21 | "type": "string" 22 | } 23 | }, 24 | "resources": [], 25 | "variables": {} 26 | } -------------------------------------------------------------------------------- /solution_template/nested/storageAccount-new.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "outputs": { 5 | "primaryEndpointBlob": { 6 | "type": "string", 7 | "value": "[reference(resourceId('Microsoft.Storage/storageAccounts',parameters('storageAccountName')),'2016-01-01').primaryEndpoints.blob]" 8 | } 9 | }, 10 | "parameters": { 11 | "storageAccountLocation": { 12 | "type": "string" 13 | }, 14 | "storageAccountName": { 15 | "type": "string" 16 | }, 17 | "storageAccountType": { 18 | "type": "string" 19 | }, 20 | "storageAccountResourceGroup": { 21 | "type": "string" 22 | } 23 | }, 24 | "resources": [ 25 | { 26 | "apiVersion": "2016-01-01", 27 | "dependsOn": [], 28 | "kind": "Storage", 29 | "location": "[parameters('storageAccountLocation')]", 30 | "name": "[parameters('storageAccountName')]", 31 | "sku": { 32 | "name": "[parameters('storageAccountType')]" 33 | }, 34 | "type": "Microsoft.Storage/storageAccounts" 35 | } 36 | ], 37 | "variables": {} 38 | } -------------------------------------------------------------------------------- /solution_template/spinnaker/createUiDefinition.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", 3 | "handler": "Microsoft.Compute.MultiVm", 4 | "version": "0.1.2-preview", 5 | "parameters": { 6 | "basics": [ 7 | { 8 | "name": "vmName", 9 | "type": "Microsoft.Common.TextBox", 10 | "label": "Name", 11 | "defaultValue": "Spinnaker", 12 | "toolTip": "", 13 | "constraints": { 14 | "required": true, 15 | "regex": "^[a-z0-9A-Z]{1,54}$", 16 | "validationMessage": "Only alphanumeric characters are allowed, and the value must be 1-54 characters long." 17 | }, 18 | "visible": true 19 | }, 20 | { 21 | "name": "vmDiskType", 22 | "type": "Microsoft.Common.DropDown", 23 | "label": "VM disk type", 24 | "defaultValue": "SSD", 25 | "toolTip": "Premium disks (SSD) are backed by solid state drives and offer consistent, low-latency performance. They provide the best balance between price and performance, and are ideal for I/O-intensive applications and production workloads. Standard disks (HDD) are backed by magnetic drives and are preferable for applications where data is accessed infrequently.", 26 | "constraints": { 27 | "allowedValues": [ 28 | { 29 | "label": "SSD", 30 | "value": "0" 31 | }, 32 | { 33 | "label": "HDD", 34 | "value": "1" 35 | } 36 | ] 37 | }, 38 | "visible": true 39 | }, 40 | { 41 | "name": "userName", 42 | "type": "Microsoft.Compute.UserNameTextBox", 43 | "label": "User name", 44 | "defaultValue": "", 45 | "toolTip": "", 46 | "constraints": { 47 | "required": true 48 | }, 49 | "osPlatform": "Linux", 50 | "visible": true 51 | }, 52 | { 53 | "name": "authType", 54 | "type": "Microsoft.Compute.CredentialsCombo", 55 | "label": { 56 | "authenticationType": "Authentication type", 57 | "password": "Password", 58 | "confirmPassword": "Confirm password", 59 | "sshPublicKey": "SSH public key" 60 | }, 61 | "toolTip": { 62 | "authenticationType": "", 63 | "password": "", 64 | "sshPublicKey": "" 65 | }, 66 | "constraints": { 67 | "required": true, 68 | "customPasswordRegex": "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d@#\\$%\\^&\\*-_!+=\\[\\]\\{\\}\\|\\\\:',\\.\\?\\/`~\"\\(\\);]{8,}$", 69 | "customValidationMessage": "The password must contain at least 8 characters, with at least 1 letter and 1 number. Alphanumerical (@#$%^&*-_!+=[]{}|\\:',.?/`~\"();)and special characters are allowed" 70 | }, 71 | "options": { 72 | "hideConfirmation": false, 73 | "hidePassword": false 74 | }, 75 | "osPlatform": "Linux", 76 | "visible": true 77 | } 78 | ], 79 | "steps": [ 80 | { 81 | "name": "firstStep", 82 | "label": "Settings", 83 | "bladeTitle": "Settings", 84 | "subLabel": { 85 | "preValidation": "Configure additional options", 86 | "postValidation": "Done" 87 | }, 88 | "elements": [ 89 | { 90 | "name": "vmSize", 91 | "type": "Microsoft.Compute.SizeSelector", 92 | "label": "Size", 93 | "toolTip": "", 94 | "recommendedSizes": [ 95 | "Standard_DS2_v2", 96 | "Standard_DS1_v2", 97 | "Standard_DS11_v2" 98 | ], 99 | "constraints": { 100 | "allowedSizes": [ 101 | "Standard_DS1_v2", 102 | "Standard_DS2_v2", 103 | "Standard_DS3_v2", 104 | "Standard_DS4_v2", 105 | "Standard_DS5_v2", 106 | "Standard_DS11_v2" 107 | ] 108 | }, 109 | "osPlatform": "Linux", 110 | "count": 1, 111 | "visible": true 112 | }, 113 | { 114 | "name": "storage_type_hdd_true", 115 | "type": "Microsoft.Common.TextBox", 116 | "label": "", 117 | "defaultValue": "Standard_LRS", 118 | "toolTip": "", 119 | "visible": false 120 | }, 121 | { 122 | "name": "storage_type_hdd_false", 123 | "type": "Microsoft.Common.TextBox", 124 | "label": "", 125 | "defaultValue": "Premium_LRS", 126 | "toolTip": "", 127 | "visible": false 128 | }, 129 | { 130 | "name": "storageAccount", 131 | "type": "Microsoft.Storage.StorageAccountSelector", 132 | "label": "Storage account", 133 | "toolTip": "Disks for Azure virtual machines are created in storage accounts", 134 | "defaultValue": { 135 | "name": "[concat('spinnaker', take( toLower(encodeBase64(guid())) , 12), string(rand(0, 999)) )]", 136 | "type": "[ first ( skip( parse('[\"Premium_LRS\", \"Standard_LRS\" ]'), int(basics('vmDiskType')) ) ) ]" 137 | }, 138 | "options": { 139 | "hideExisting": false 140 | }, 141 | "constraints": { 142 | "allowedTypes": [ 143 | "Standard_LRS", 144 | "Standard_GRS", 145 | "Standard_RAGRS", 146 | "Premium_LRS" 147 | ] 148 | }, 149 | "visible": true 150 | }, 151 | { 152 | "name": "publicIP", 153 | "type": "Microsoft.Network.PublicIpAddressCombo", 154 | "label": { 155 | "publicIpAddress": "Public IP address", 156 | "domainNameLabel": "Domain name label" 157 | }, 158 | "toolTip": { 159 | "publicIpAddress": "", 160 | "domainNameLabel": "" 161 | }, 162 | "defaultValue": { 163 | "publicIpAddressName": "SpinnakerIP", 164 | "domainNameLabel": "" 165 | }, 166 | "constraints": { 167 | "required": { 168 | "domainNameLabel": true 169 | } 170 | }, 171 | "options": { 172 | "hideNone": true, 173 | "hideDomainNameLabel": false, 174 | "hideExisting": false 175 | }, 176 | "visible": true 177 | } 178 | ] 179 | } 180 | ], 181 | "outputs": { 182 | "adminPassword": "[basics('authType').password]", 183 | "adminSSHPublicKey": "[basics('authType').sshPublicKey]", 184 | "adminUserName": "[basics('userName')]", 185 | "authenticationType": "[basics('authType').authenticationType]", 186 | "dnsPrefix": "[steps('firstStep').publicIP.domainNameLabel]", 187 | "publicIPName": "[steps('firstStep').publicIP.name]", 188 | "publicIPNewOrExisting": "[steps('firstStep').publicIP.newOrExistingOrNone]", 189 | "publicIPResourceGroup": "[steps('firstStep').publicIP.resourceGroup]", 190 | "storageAccountName": "[steps('firstStep').storageAccount.name]", 191 | "storageAccountNewOrExisting": "[steps('firstStep').storageAccount.newOrExisting]", 192 | "storageAccountType": "[steps('firstStep').storageAccount.type]", 193 | "storageAccountResourceGroup": "[steps('firstStep').storageAccount.resourceGroup]", 194 | "vmName": "[basics('vmName')]", 195 | "vmSize": "[steps('firstStep').vmSize]", 196 | "location": "[location()]" 197 | } 198 | } 199 | } -------------------------------------------------------------------------------- /solution_template/spinnaker/mainTemplate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "_artifactsLocation": { 6 | "metadata": { 7 | "description": "Artifacts location" 8 | }, 9 | "defaultValue": "https://raw.githubusercontent.com/Azure/azure-devops-utils/v0.26.0/", 10 | "type": "string" 11 | }, 12 | "_artifactsLocationSasToken": { 13 | "metadata": { 14 | "description": "Artifacts Sas token" 15 | }, 16 | "defaultValue": "", 17 | "type": "string" 18 | }, 19 | "adminPassword": { 20 | "metadata": { 21 | "description": "Password for the Virtual Machine. Will be used only if authenticationType is 'password'" 22 | }, 23 | "defaultValue": "", 24 | "type": "securestring" 25 | }, 26 | "adminSSHPublicKey": { 27 | "metadata": { 28 | "description": "Public SSH key for the Virtual Machine. Will be used only if authenticationType is 'sshPublicKey'" 29 | }, 30 | "defaultValue": "", 31 | "type": "string" 32 | }, 33 | "adminUserName": { 34 | "metadata": { 35 | "description": "User name for the Virtual Machine" 36 | }, 37 | "type": "string" 38 | }, 39 | "authenticationType": { 40 | "metadata": { 41 | "description": "Authentication type (can be 'password' or 'sshPublicKey')" 42 | }, 43 | "type": "string" 44 | }, 45 | "dnsPrefix": { 46 | "metadata": { 47 | "description": "Unique DNS Name for the Public IP used to access the Spinnaker Virtual Machine." 48 | }, 49 | "type": "string" 50 | }, 51 | "publicIPName": { 52 | "metadata": { 53 | "description": "Public IP name" 54 | }, 55 | "type": "string" 56 | }, 57 | "publicIPNewOrExisting": { 58 | "metadata": { 59 | "description": "Should be 'new' if the public IP has to be created" 60 | }, 61 | "type": "string" 62 | }, 63 | "publicIPResourceGroup": { 64 | "metadata": { 65 | "description": "The Public IP Resource Group" 66 | }, 67 | "type": "string" 68 | }, 69 | "storageAccountName": { 70 | "metadata": { 71 | "description": "Storage Account Name" 72 | }, 73 | "type": "string" 74 | }, 75 | "storageAccountNewOrExisting": { 76 | "metadata": { 77 | "description": "Should be 'new' or 'existing'" 78 | }, 79 | "type": "string" 80 | }, 81 | "storageAccountType": { 82 | "defaultValue": "Standard_LRS", 83 | "metadata": { 84 | "description": "Storage Account Type" 85 | }, 86 | "type": "string" 87 | }, 88 | "storageAccountResourceGroup": { 89 | "metadata": { 90 | "description": "The Storage Account Resource Group" 91 | }, 92 | "type": "string" 93 | }, 94 | "vmName": { 95 | "metadata": { 96 | "description": "Virtual Machine Name (also used as a prefix for other resources)" 97 | }, 98 | "type": "string" 99 | }, 100 | "vmSize": { 101 | "metadata": { 102 | "description": "Virtual Machine Size" 103 | }, 104 | "type": "string" 105 | }, 106 | "location": { 107 | "metadata": { 108 | "description": "Azure location where to deploy the resources" 109 | }, 110 | "type": "string", 111 | "defaultValue": "[resourceGroup().location]" 112 | } 113 | }, 114 | "variables": { 115 | "_extensionScript": "101-spinnaker.sh", 116 | "frontEndNSGName": "[concat(parameters('vmName'), 'NSG')]", 117 | "nestedUrl": "[concat(parameters('_artifactsLocation'),'/solution_template/nested')]", 118 | "nicName": "[concat(parameters('vmName'), 'VMNic')]", 119 | "OSDiskName": "[concat(parameters('vmName'), 'OSDisk-', uniqueString(resourceGroup().id, deployment().name))]", 120 | "publicIPDeploymentName": "[concat(parameters('publicIPName'), '-publicIP-deploy')]", 121 | "storageDeploymentName": "[concat(parameters('storageAccountName'), '-storage-deploy')]", 122 | "subnetName": "[concat(parameters('vmName'), 'Subnet')]", 123 | "subnetPrefix": "10.0.0.0/24", 124 | "templateUrlIP": "[concat(variables('nestedUrl'),'/publicIP-',parameters('publicIPNewOrExisting'),'.json',parameters('_artifactsLocationSasToken'))]", 125 | "templateUrlStorage": "[concat(variables('nestedUrl'),'/storageAccount-',parameters('storageAccountNewOrExisting'),'.json',parameters('_artifactsLocationSasToken'))]", 126 | "templateUrlVM": "[concat(variables('nestedUrl'),'/VM-',parameters('authenticationType'),'.json',parameters('_artifactsLocationSasToken'))]", 127 | "templateUrlVMInit": "[concat(variables('nestedUrl'), '/spinnaker-VM-Init.json', parameters('_artifactsLocationSasToken'))]", 128 | "ubuntuSku": "14.04.5-LTS", 129 | "virtualNetworkName": "[concat(parameters('vmName'), 'VNET')]", 130 | "virtualNetworkPrefix": "10.0.0.0/16", 131 | "vmDeploymentName": "[concat(parameters('vmName'), '-VM-deploy')]", 132 | "vmInitDeploymentName": "[concat(parameters('vmName'), '-VM-Init')]", 133 | "vmPrivateIP": "10.0.0.5", 134 | "vmStorageAccountContainerName": "vhds" 135 | }, 136 | "resources": [ 137 | { 138 | "apiVersion": "2016-09-01", 139 | "dependsOn": [], 140 | "name": "[variables('storageDeploymentName')]", 141 | "properties": { 142 | "mode": "Incremental", 143 | "parameters": { 144 | "storageAccountName": { 145 | "value": "[parameters('storageAccountName')]" 146 | }, 147 | "storageAccountType": { 148 | "value": "[parameters('storageAccountType')]" 149 | }, 150 | "storageAccountLocation": { 151 | "value": "[parameters('location')]" 152 | }, 153 | "storageAccountResourceGroup": { 154 | "value": "[parameters('storageAccountResourceGroup')]" 155 | } 156 | }, 157 | "templateLink": { 158 | "contentVersion": "1.0.0.0", 159 | "uri": "[variables('templateUrlStorage')]" 160 | } 161 | }, 162 | "type": "Microsoft.Resources/deployments" 163 | }, 164 | { 165 | "apiVersion": "2016-09-01", 166 | "dependsOn": [], 167 | "name": "[variables('publicIPDeploymentName')]", 168 | "properties": { 169 | "mode": "Incremental", 170 | "parameters": { 171 | "publicIPName": { 172 | "value": "[parameters('publicIPName')]" 173 | }, 174 | "publicIPLocation": { 175 | "value": "[parameters('location')]" 176 | }, 177 | "dnsPrefix": { 178 | "value": "[parameters('dnsPrefix')]" 179 | }, 180 | "publicIPResourceGroup": { 181 | "value": "[parameters('publicIPResourceGroup')]" 182 | } 183 | }, 184 | "templateLink": { 185 | "contentVersion": "1.0.0.0", 186 | "uri": "[variables('templateUrlIP')]" 187 | } 188 | }, 189 | "type": "Microsoft.Resources/deployments" 190 | }, 191 | { 192 | "apiVersion": "2015-06-15", 193 | "location": "[parameters('location')]", 194 | "name": "[variables('frontEndNSGName')]", 195 | "properties": { 196 | "securityRules": [ 197 | { 198 | "name": "ssh-rule", 199 | "properties": { 200 | "access": "Allow", 201 | "description": "Allow SSH", 202 | "destinationAddressPrefix": "*", 203 | "destinationPortRange": "22", 204 | "direction": "Inbound", 205 | "priority": 100, 206 | "protocol": "Tcp", 207 | "sourceAddressPrefix": "Internet", 208 | "sourcePortRange": "*" 209 | } 210 | }, 211 | { 212 | "name": "http-rule", 213 | "properties": { 214 | "access": "Allow", 215 | "description": "Allow HTTP", 216 | "destinationAddressPrefix": "*", 217 | "destinationPortRange": "80", 218 | "direction": "Inbound", 219 | "priority": 101, 220 | "protocol": "Tcp", 221 | "sourceAddressPrefix": "Internet", 222 | "sourcePortRange": "*" 223 | } 224 | } 225 | ] 226 | }, 227 | "tags": { 228 | "displayName": "NSG - Front End" 229 | }, 230 | "type": "Microsoft.Network/networkSecurityGroups" 231 | }, 232 | { 233 | "apiVersion": "2016-03-30", 234 | "dependsOn": [ 235 | "[resourceId('Microsoft.Network/networkSecurityGroups', variables('frontEndNSGName'))]" 236 | ], 237 | "location": "[parameters('location')]", 238 | "name": "[variables('virtualNetworkName')]", 239 | "properties": { 240 | "addressSpace": { 241 | "addressPrefixes": [ 242 | "[variables('virtualNetworkPrefix')]" 243 | ] 244 | }, 245 | "subnets": [ 246 | { 247 | "name": "[variables('subnetName')]", 248 | "properties": { 249 | "addressPrefix": "[variables('subnetPrefix')]", 250 | "networkSecurityGroup": { 251 | "id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('frontEndNSGName'))]" 252 | } 253 | } 254 | } 255 | ] 256 | }, 257 | "tags": { 258 | "displayName": "[variables('virtualNetworkName')]" 259 | }, 260 | "type": "Microsoft.Network/virtualNetworks" 261 | }, 262 | { 263 | "apiVersion": "2016-03-30", 264 | "dependsOn": [ 265 | "[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]", 266 | "[resourceId('Microsoft.Resources/deployments', variables('publicIPDeploymentName'))]" 267 | ], 268 | "location": "[parameters('location')]", 269 | "name": "[variables('nicName')]", 270 | "properties": { 271 | "ipConfigurations": [ 272 | { 273 | "name": "ipconfig1", 274 | "properties": { 275 | "privateIpAddress": "[variables('vmPrivateIP')]", 276 | "privateIPAllocationMethod": "Static", 277 | "publicIPAddress": { 278 | "id": "[resourceId(parameters('publicIPResourceGroup'),'Microsoft.Network/publicIPAddresses', parameters('publicIPName'))]" 279 | }, 280 | "subnet": { 281 | "id": "[concat(resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName')),'/subnets/',variables('subnetName'))]" 282 | } 283 | } 284 | } 285 | ] 286 | }, 287 | "tags": { 288 | "displayName": "[variables('nicName')]" 289 | }, 290 | "type": "Microsoft.Network/networkInterfaces" 291 | }, 292 | { 293 | "apiVersion": "2016-09-01", 294 | "dependsOn": [ 295 | "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]", 296 | "[resourceId('Microsoft.Resources/deployments', variables('publicIPDeploymentName'))]", 297 | "[resourceId('Microsoft.Resources/deployments', variables('storageDeploymentName'))]" 298 | ], 299 | "name": "[variables('vmDeploymentName')]", 300 | "properties": { 301 | "mode": "Incremental", 302 | "parameters": { 303 | "adminPassword": { 304 | "value": "[parameters('adminPassword')]" 305 | }, 306 | "adminSSHPublicKey": { 307 | "value": "[parameters('adminSSHPublicKey')]" 308 | }, 309 | "adminUserName": { 310 | "value": "[parameters('adminUserName')]" 311 | }, 312 | "networkInterfaceId": { 313 | "value": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" 314 | }, 315 | "osDiskVHDUri": { 316 | "value": "[concat(reference(variables('storageDeploymentName')).outputs.primaryEndpointBlob.value, variables('vmStorageAccountContainerName'), '/', variables('OSDiskName'), '.vhd')]" 317 | }, 318 | "ubuntuSku": { 319 | "value": "[variables('ubuntuSku')]" 320 | }, 321 | "vmLocation": { 322 | "value": "[parameters('location')]" 323 | }, 324 | "vmName": { 325 | "value": "[parameters('vmName')]" 326 | }, 327 | "vmSize": { 328 | "value": "[parameters('vmSize')]" 329 | } 330 | }, 331 | "templateLink": { 332 | "contentVersion": "1.0.0.0", 333 | "uri": "[variables('templateUrlVM')]" 334 | } 335 | }, 336 | "type": "Microsoft.Resources/deployments" 337 | }, 338 | { 339 | "apiVersion": "2016-09-01", 340 | "dependsOn": [ 341 | "[resourceId('Microsoft.Resources/deployments', variables('vmDeploymentName'))]" 342 | ], 343 | "name": "[variables('vmInitDeploymentName')]", 344 | "properties": { 345 | "mode": "Incremental", 346 | "parameters": { 347 | "location": { 348 | "value": "[parameters('location')]" 349 | }, 350 | "vmName": { 351 | "value": "[parameters('vmName')]" 352 | }, 353 | "_extensionScript": { 354 | "value": "[variables('_extensionScript')]" 355 | }, 356 | "storageAccountName": { 357 | "value": "[parameters('storageAccountName')]" 358 | }, 359 | "storageAccountResourceGroup": { 360 | "value": "[parameters('storageAccountResourceGroup')]" 361 | }, 362 | "adminUsername": { 363 | "value": "[parameters('adminUsername')]" 364 | }, 365 | "_artifactsLocation": { 366 | "value": "[parameters('_artifactsLocation')]" 367 | }, 368 | "_artifactsLocationSasToken": { 369 | "value": "[parameters('_artifactsLocationSasToken')]" 370 | } 371 | }, 372 | "templateLink": { 373 | "contentVersion": "1.0.0.0", 374 | "uri": "[variables('templateUrlVMInit')]" 375 | } 376 | }, 377 | "type": "Microsoft.Resources/deployments" 378 | } 379 | ], 380 | "outputs": { 381 | "spinnakerVmFQDN": { 382 | "type": "string", 383 | "value": "[reference(variables('publicIPDeploymentName')).outputs.fqdn.value]" 384 | }, 385 | "ssh": { 386 | "type": "string", 387 | "value": "[concat('ssh -L 9000:localhost:9000 -L 8084:localhost:8084 ', parameters('adminUsername'), '@', reference(variables('publicIPDeploymentName')).outputs.fqdn.value)]" 388 | } 389 | } 390 | } 391 | -------------------------------------------------------------------------------- /spinnaker/add_k8s_pipeline/README.md: -------------------------------------------------------------------------------- 1 | # Add Kubernetes Pipeline 2 | 3 | Adds a Kubernetes pipeline with three main stages: 4 | 5 | 1. Deploy to a development environment 6 | 1. Wait for manual judgement 7 | 1. Deploy to a production environment 8 | 9 | The pipeline will be triggered by any new tag pushed to the target repository and will also clean up previous deployments. 10 | 11 | ## Prerequisites 12 | This must be executed on a machine with a running Spinnaker instance. 13 | 14 | ## Arguments 15 | | Name | Description | 16 | |---|---| 17 | | --account_name
-an | The Spinnaker account name for your registry | 18 | | --registry
-rg | The Azure Container Registry url, for example 'sample-microsoft.azurecr.io'. | 19 | | --repository
-rp | The repository, for example 'Fabrikam/app1'. Any new tag pushed to this repository will trigger the pipeline. | 20 | | --port
-p | (optional) The port used when creating load balancers for the pipeline. The container deployed by your pipeline is expected to be listening on this port. The default is '8000'. | 21 | | --user_name
-un | (optional) The user name for creating the pipeline. The default is '[anonymous]'. | 22 | | --user_email
-ue | (optional) The user email for creating the pipeline. The default is 'anonymous@Fabrikam.com'. | 23 | | --artifacts_location
-al | (optional) The url for referencing other scripts/artifacts. The default is this github repository. | 24 | | --sas_token
-st | (optional) A sas token needed if the artifacts location is private. | 25 | 26 | ## Example usage 27 | ```bash 28 | ./add_k8s_pipeline.sh --acount-name "azure-container-registry" --registry "sample-microsoft.azurecr.io" --repository "Fabrikam/application1" 29 | ``` 30 | 31 | ## Questions/Comments? azdevopspub@microsoft.com -------------------------------------------------------------------------------- /spinnaker/add_k8s_pipeline/add_k8s_pipeline.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function print_usage() { 4 | cat <&2 25 | print_usage 26 | exit -1 27 | fi 28 | } 29 | 30 | # Set defaults 31 | port="8000" 32 | user_name="[anonymous]" 33 | user_email="anonymous@Fabrikam.com" 34 | artifacts_location="https://raw.githubusercontent.com/Azure/azure-devops-utils/master/" 35 | 36 | while [[ $# > 0 ]] 37 | do 38 | key="$1" 39 | shift 40 | case $key in 41 | --account_name|-an) 42 | account_name="$1" 43 | shift 44 | ;; 45 | --registry|-rg) 46 | registry="$1" 47 | # Remove http prefix and trailing slash from registry if they exist 48 | registry=${registry#"https://"} 49 | registry=${registry#"http://"} 50 | registry=${registry%"/"} 51 | shift 52 | ;; 53 | --repository|-rp) 54 | repository="$1" 55 | shift 56 | ;; 57 | --port|-p) 58 | port="$1" 59 | shift 60 | ;; 61 | --user_name|-un) 62 | user_name="$1" 63 | shift 64 | ;; 65 | --user_email|-ue) 66 | user_email="$1" 67 | shift 68 | ;; 69 | --artifacts_location|-al) 70 | artifacts_location="$1" 71 | shift 72 | ;; 73 | --sas_token|-st) 74 | artifacts_location_sas_token="$1" 75 | shift 76 | ;; 77 | --help|-help|-h) 78 | print_usage 79 | exit 13 80 | ;; 81 | *) 82 | echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 83 | exit -1 84 | esac 85 | done 86 | 87 | throw_if_empty --account_name $account_name 88 | throw_if_empty --registry $registry 89 | throw_if_empty --repository $repository 90 | throw_if_empty --port $port 91 | throw_if_empty --user_name $user_name 92 | throw_if_empty --user_email $user_email 93 | 94 | # Validate and parse repository 95 | if [[ "$repository" =~ ^([^[:space:]\/]+)\/([^[:space:]\/]+)$ ]]; then 96 | organization="${BASH_REMATCH[1]}" 97 | app_name="${BASH_REMATCH[2]}" 98 | app_name=$(echo "${app_name,,}" | tr -cd '[[:alnum:]]') # Convert to lowercase and remove symbols from application name. Kubernetes only supports ^[a-z0-9]+$ 99 | else 100 | echo "Expected repository to be of the form 'organization/applicationname', but instead received '$repository'." 1>&2 101 | exit 1 102 | fi 103 | 104 | # Connect to k8s cluster in the background 105 | hal deploy connect --service-names gate & 106 | timeout=30 107 | echo "while !(nc -z localhost 8084); do sleep 1; done" | timeout $timeout bash 108 | return_value=$? 109 | if [ $return_value -ne 0 ]; then 110 | >&2 echo "Failed to connect to Spinnaker within '$timeout' seconds." 111 | exit $return_value 112 | fi 113 | 114 | # Create application 115 | application_data=$(curl -s ${artifacts_location}spinnaker/add_k8s_pipeline/application.json${artifacts_location_sas_token}) 116 | application_data=${application_data//REPLACE_APP_NAME/$app_name} 117 | application_data=${application_data//REPLACE_USER_NAME/$user_name} 118 | application_data=${application_data//REPLACE_USER_EMAIL/$user_email} 119 | curl -X POST -H "Content-type: application/json" --data "$application_data" http://localhost:8084/applications/${app_name}/tasks 120 | 121 | # Create pipeline 122 | pipeline_data=$(curl -s ${artifacts_location}spinnaker/add_k8s_pipeline/pipeline.json${artifacts_location_sas_token}) 123 | pipeline_data=${pipeline_data//REPLACE_APP_NAME/$app_name} 124 | pipeline_data=${pipeline_data//REPLACE_ACCOUNT_NAME/$account_name} 125 | pipeline_data=${pipeline_data//REPLACE_REGISTRY/$registry} 126 | pipeline_data=${pipeline_data//REPLACE_REPOSITORY/$repository} 127 | pipeline_data=${pipeline_data//REPLACE_ORGANIZATION/$organization} 128 | pipeline_data=${pipeline_data//REPLACE_PORT/$port} 129 | curl -X POST -H "Content-type: application/json" --data "$pipeline_data" http://localhost:8084/pipelines 130 | 131 | # Create dev load balancer 132 | load_balancer_data=$(curl -s ${artifacts_location}spinnaker/add_k8s_pipeline/load_balancer.json${artifacts_location_sas_token}) 133 | dev_load_balancer_data=${load_balancer_data//REPLACE_APP_NAME/$app_name} 134 | dev_load_balancer_data=${dev_load_balancer_data//REPLACE_USER_NAME/$user_name} 135 | dev_load_balancer_data=${dev_load_balancer_data//REPLACE_PORT/$port} 136 | dev_load_balancer_data=${dev_load_balancer_data//REPLACE_STACK/"dev"} 137 | dev_load_balancer_data=${dev_load_balancer_data//REPLACE_SERVICE_TYPE/"ClusterIP"} 138 | curl -X POST -H "Content-type: application/json" --data "$dev_load_balancer_data" http://localhost:8084/applications/${app_name}/tasks 139 | 140 | # Create prod load balancer 141 | prod_load_balancer_data=${load_balancer_data//REPLACE_APP_NAME/$app_name} 142 | prod_load_balancer_data=${prod_load_balancer_data//REPLACE_USER_NAME/$user_name} 143 | prod_load_balancer_data=${prod_load_balancer_data//REPLACE_PORT/$port} 144 | prod_load_balancer_data=${prod_load_balancer_data//REPLACE_STACK/"prod"} 145 | prod_load_balancer_data=${prod_load_balancer_data//REPLACE_SERVICE_TYPE/"LoadBalancer"} 146 | curl -X POST -H "Content-type: application/json" --data "$prod_load_balancer_data" http://localhost:8084/applications/${app_name}/tasks 147 | 148 | # Stop background connection to Spinnaker 149 | pkill kubectl -------------------------------------------------------------------------------- /spinnaker/add_k8s_pipeline/application.json: -------------------------------------------------------------------------------- 1 | { 2 | "job": [ 3 | { 4 | "type": "createApplication", 5 | "account": "my-kubernetes-account", 6 | "application": { 7 | "accounts": "my-kubernetes-account", 8 | "cloudProviders": "kubernetes", 9 | "instancePort": 80, 10 | "name": "REPLACE_APP_NAME", 11 | "email": "REPLACE_USER_EMAIL" 12 | }, 13 | "user": "REPLACE_USER_NAME" 14 | } 15 | ], 16 | "application": "REPLACE_APP_NAME", 17 | "description": "Create Application: REPLACE_APP_NAME" 18 | } -------------------------------------------------------------------------------- /spinnaker/add_k8s_pipeline/load_balancer.json: -------------------------------------------------------------------------------- 1 | { 2 | "job": [ 3 | { 4 | "provider": "kubernetes", 5 | "stack": "REPLACE_STACK", 6 | "detail": "", 7 | "serviceType": "REPLACE_SERVICE_TYPE", 8 | "account": "my-kubernetes-account", 9 | "namespace": "default", 10 | "ports": [ 11 | { 12 | "protocol": "TCP", 13 | "port": 80, 14 | "name": "http", 15 | "targetPort": REPLACE_PORT 16 | } 17 | ], 18 | "externalIps": [], 19 | "sessionAffinity": "None", 20 | "clusterIp": "", 21 | "loadBalancerIp": "", 22 | "name": "REPLACE_APP_NAME-REPLACE_STACK", 23 | "serviceAnnotations": {}, 24 | "cloudProvider": "kubernetes", 25 | "availabilityZones": { 26 | "default": [ 27 | "default" 28 | ] 29 | }, 30 | "type": "upsertLoadBalancer", 31 | "user": "REPLACE_USER_NAME" 32 | } 33 | ], 34 | "application": "REPLACE_APP_NAME", 35 | "description": "Create Load Balancer: REPLACE_APP_NAME-REPLACE_STACK" 36 | } -------------------------------------------------------------------------------- /spinnaker/add_k8s_pipeline/pipeline.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "REPLACE_APP_NAME Source to Prod", 3 | "application": "REPLACE_APP_NAME", 4 | "limitConcurrent": true, 5 | "keepWaitingPipelines": false, 6 | "parallel": true, 7 | "executionEngine": "v2", 8 | "index": 0, 9 | "stages": [ 10 | { 11 | "type": "deploy", 12 | "clusters": [ 13 | { 14 | "deployment": null, 15 | "stack": "dev", 16 | "region": "default", 17 | "cloudProvider": "kubernetes", 18 | "namespace": "default", 19 | "securityContext": null, 20 | "provider": "kubernetes", 21 | "restartPolicy": null, 22 | "targetSize": 1, 23 | "application": "REPLACE_APP_NAME", 24 | "loadBalancers": [ 25 | "REPLACE_APP_NAME-dev" 26 | ], 27 | "podAnnotations": {}, 28 | "securityGroups": [], 29 | "volumeSources": [], 30 | "strategy": "", 31 | "freeFormDetails": null, 32 | "account": "my-kubernetes-account", 33 | "interestingHealthProviderNames": [ 34 | "KubernetesContainer", 35 | "KubernetesPod" 36 | ], 37 | "replicaSetAnnotations": {}, 38 | "containers": [ 39 | { 40 | "limits": { 41 | "memory": null, 42 | "cpu": null 43 | }, 44 | "volumeMounts": [], 45 | "livenessProbe": null, 46 | "command": [], 47 | "args": [], 48 | "envVars": [], 49 | "ports": [ 50 | { 51 | "hostIp": null, 52 | "protocol": "TCP", 53 | "name": "http", 54 | "hostPort": null, 55 | "containerPort": REPLACE_PORT 56 | } 57 | ], 58 | "imagePullPolicy": "IFNOTPRESENT", 59 | "name": "REPLACE_APP_NAME", 60 | "imageDescription": { 61 | "imageId": "REPLACE_REGISTRY/REPLACE_REPOSITORY (Tag resolved at runtime)", 62 | "fromTrigger": true, 63 | "registry": "REPLACE_REGISTRY", 64 | "repository": "REPLACE_REPOSITORY", 65 | "tag": null, 66 | "account": "REPLACE_ACCOUNT_NAME" 67 | }, 68 | "requests": { 69 | "cpu": null, 70 | "memory": null 71 | }, 72 | "readinessProbe": { 73 | "handler": { 74 | "httpGetAction": { 75 | "uriScheme": "HTTP", 76 | "port": REPLACE_PORT, 77 | "path": "/" 78 | }, 79 | "type": "HTTP", 80 | "tcpSocketAction": { 81 | "port": 80 82 | }, 83 | "execAction": { 84 | "commands": [] 85 | } 86 | }, 87 | "successThreshold": 1, 88 | "failureThreshold": 3, 89 | "timeoutSeconds": 1, 90 | "periodSeconds": 10, 91 | "initialDelaySeconds": 0 92 | } 93 | } 94 | ] 95 | } 96 | ], 97 | "refId": "1", 98 | "name": "Deploy to Dev", 99 | "requisiteStageRefIds": [] 100 | }, 101 | { 102 | "type": "destroyServerGroup", 103 | "credentials": "my-kubernetes-account", 104 | "refId": "2", 105 | "requisiteStageRefIds": [ 106 | "1" 107 | ], 108 | "cloudProviderType": "kubernetes", 109 | "cloudProvider": "kubernetes", 110 | "cluster": "REPLACE_APP_NAME-dev", 111 | "continuePipeline": true, 112 | "failPipeline": false, 113 | "namespaces": [ 114 | "default" 115 | ], 116 | "name": "Destroy Previous Dev Cluster", 117 | "interestingHealthProviderNames": [ 118 | "KubernetesService" 119 | ], 120 | "target": "ancestor_asg_dynamic" 121 | }, 122 | { 123 | "continuePipeline": false, 124 | "failPipeline": false, 125 | "judgmentInputs": [], 126 | "name": "Manual Judgment", 127 | "notifications": [], 128 | "refId": "3", 129 | "type": "manualJudgment", 130 | "requisiteStageRefIds": [ 131 | "1" 132 | ], 133 | "instructions": "Verify the dev server group looks good. Run `kubectl proxy` and navigate to this url to test: http://localhost:8001/api/v1/proxy/namespaces/default/services/REPLACE_APP_NAME-dev:80/" 134 | }, 135 | { 136 | "cloudProvider": "kubernetes", 137 | "cloudProviderType": "kubernetes", 138 | "selectionStrategy": "NEWEST", 139 | "name": "Find Image from Dev Cluster", 140 | "onlyEnabled": true, 141 | "namespaces": [ 142 | "default" 143 | ], 144 | "cluster": "REPLACE_APP_NAME-dev", 145 | "refId": "4", 146 | "credentials": "my-kubernetes-account", 147 | "type": "findImage", 148 | "requisiteStageRefIds": [ 149 | "3" 150 | ], 151 | "imageNamePattern": ".*" 152 | }, 153 | { 154 | "requisiteStageRefIds": [ 155 | "4" 156 | ], 157 | "name": "Deploy to Prod", 158 | "refId": "5", 159 | "type": "deploy", 160 | "clusters": [ 161 | { 162 | "replicaSetAnnotations": {}, 163 | "interestingHealthProviderNames": [ 164 | "KubernetesContainer", 165 | "KubernetesPod" 166 | ], 167 | "containers": [ 168 | { 169 | "command": [], 170 | "livenessProbe": null, 171 | "volumeMounts": [], 172 | "limits": { 173 | "cpu": null, 174 | "memory": null 175 | }, 176 | "readinessProbe": { 177 | "handler": { 178 | "httpGetAction": { 179 | "path": "/", 180 | "port": REPLACE_PORT, 181 | "uriScheme": "HTTP" 182 | }, 183 | "type": "HTTP", 184 | "tcpSocketAction": { 185 | "port": 80 186 | }, 187 | "execAction": { 188 | "commands": [] 189 | } 190 | }, 191 | "failureThreshold": 3, 192 | "successThreshold": 1, 193 | "initialDelaySeconds": 0, 194 | "periodSeconds": 10, 195 | "timeoutSeconds": 1 196 | }, 197 | "requests": { 198 | "memory": null, 199 | "cpu": null 200 | }, 201 | "imageDescription": { 202 | "repository": "Find Image from Dev Cluster", 203 | "cluster": "REPLACE_APP_NAME-dev", 204 | "stageId": "4", 205 | "imageId": "REPLACE_APP_NAME-dev .*", 206 | "fromContext": true, 207 | "pattern": ".*" 208 | }, 209 | "name": "REPLACE_APP_NAME", 210 | "ports": [ 211 | { 212 | "hostPort": null, 213 | "containerPort": REPLACE_PORT, 214 | "hostIp": null, 215 | "protocol": "TCP", 216 | "name": "http" 217 | } 218 | ], 219 | "imagePullPolicy": "IFNOTPRESENT", 220 | "envVars": [], 221 | "args": [] 222 | } 223 | ], 224 | "freeFormDetails": null, 225 | "account": "my-kubernetes-account", 226 | "strategy": "", 227 | "volumeSources": [], 228 | "podAnnotations": {}, 229 | "securityGroups": [], 230 | "targetSize": 1, 231 | "loadBalancers": [ 232 | "REPLACE_APP_NAME-prod" 233 | ], 234 | "application": "REPLACE_APP_NAME", 235 | "restartPolicy": null, 236 | "securityContext": null, 237 | "provider": "kubernetes", 238 | "cloudProvider": "kubernetes", 239 | "namespace": "default", 240 | "region": "default", 241 | "deployment": null, 242 | "stack": "prod" 243 | } 244 | ] 245 | }, 246 | { 247 | "type": "disableCluster", 248 | "credentials": "my-kubernetes-account", 249 | "remainingEnabledServerGroups": 1, 250 | "refId": "6", 251 | "preferLargerOverNewer": "false", 252 | "requisiteStageRefIds": [ 253 | "5" 254 | ], 255 | "cloudProviderType": "kubernetes", 256 | "cloudProvider": "kubernetes", 257 | "cluster": "REPLACE_APP_NAME-prod", 258 | "namespaces": [ 259 | "default" 260 | ], 261 | "name": "Disable Previous Prod Cluster", 262 | "interestingHealthProviderNames": [ 263 | "KubernetesService" 264 | ] 265 | } 266 | ], 267 | "triggers": [ 268 | { 269 | "type": "docker", 270 | "tag": null, 271 | "account": "REPLACE_ACCOUNT_NAME", 272 | "repository": "REPLACE_REPOSITORY", 273 | "registry": "REPLACE_REGISTRY", 274 | "enabled": true, 275 | "organization": "REPLACE_ORGANIZATION" 276 | } 277 | ] 278 | } -------------------------------------------------------------------------------- /spinnaker/copy_kube_config/README.md: -------------------------------------------------------------------------------- 1 | # Copy kubeconfig file 2 | 3 | Programatically copies a kubeconfig file from an Azure Container Service Kubernetes cluster to a Spinnaker machine. 4 | 5 | >**Note:** This script is only intended for use when copying the kubeconfig programatically with a Service Principal (aka when you do not have access to the ssh private key). If you want to do this manually, you can simply use 'scp'. 6 | 7 | ## Prerequisites 8 | This must be executed on a machine with an existing Spinnaker instance. The 'az' cli must be installed and you must already be logged in with the correct subscription set. 9 | 10 | ## Arguments 11 | | Name | Description | 12 | |---|---| 13 | | --user_name
-un | The admin user name for the Kubernetes cluster. | 14 | | --resource_group
-rg | The resource group containing the Kubernetes cluster. | 15 | | --master_fqdn
-mf | The FQDN for the master VMs in the Kubernetes cluster. | 16 | 17 | ## Example usage 18 | ```bash 19 | ./copy_kube_config.sh --user_name "adminuser" --resource_group "resourcegroup" --master_fqdn "samplemgmt.westus.cloudapp.azure.com" 20 | ``` 21 | 22 | ## Questions/Comments? azdevopspub@microsoft.com -------------------------------------------------------------------------------- /spinnaker/copy_kube_config/copy_kube_config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function print_usage() { 4 | cat <&2 22 | print_usage 23 | exit -1 24 | fi 25 | } 26 | 27 | while [[ $# > 0 ]] 28 | do 29 | key="$1" 30 | shift 31 | case $key in 32 | --user_name|-un) 33 | admin_user_name="$1" 34 | shift 35 | ;; 36 | --resource_group|-rg) 37 | resource_group="$1" 38 | shift 39 | ;; 40 | --master_fqdn|-mf) 41 | master_fqdn="$1" 42 | shift 43 | ;; 44 | --help|-help|-h) 45 | print_usage 46 | exit 13 47 | ;; 48 | *) 49 | echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 50 | exit -1 51 | esac 52 | done 53 | 54 | throw_if_empty --user-name $admin_user_name 55 | throw_if_empty --resource_group $resource_group 56 | throw_if_empty --master_fqdn $master_fqdn 57 | 58 | config_path="/home/$admin_user_name/.kube/config" 59 | 60 | # Setup temporary credentials to access kubernetes master vms 61 | temp_user_name=$(uuidgen | sed 's/-//g') 62 | temp_key_path=$(mktemp -d)/temp_key 63 | ssh-keygen -t rsa -N "" -f $temp_key_path -V "+1d" 64 | temp_pub_key=$(cat ${temp_key_path}.pub) 65 | 66 | master_vm_ids=$(az vm list -g "$resource_group" --query "[].id" -o tsv | grep "k8s-master-") 67 | >&2 echo "Master VM ids: $master_vm_ids" 68 | 69 | # Enable temporary credentials on every kubernetes master vm (since we don't know which vm will be used when we scp) 70 | az vm user update -u "$temp_user_name" --ssh-key-value "$temp_pub_key" --ids "$master_vm_ids" 71 | 72 | # Copy kube config over from master kubernetes cluster and mark readable 73 | sudo mkdir -p $(dirname "$config_path") 74 | sudo sh -c "ssh -o StrictHostKeyChecking=no -i \"$temp_key_path\" $temp_user_name@$master_fqdn sudo cat \"$config_path\" > \"$config_path\"" 75 | 76 | # Remove temporary credentials on every kubernetes master vm 77 | az vm user delete -u "$temp_user_name" --ids "$master_vm_ids" 78 | 79 | # Delete temp key on spinnaker vm 80 | rm $temp_key_path 81 | rm ${temp_key_path}.pub 82 | 83 | if [ ! -s "$config_path" ]; then 84 | >&2 echo "Failed to copy kubeconfig for kubernetes cluster." 85 | exit -1 86 | fi 87 | 88 | sudo chmod +r "$config_path" -------------------------------------------------------------------------------- /spinnaker/install_halyard/README.md: -------------------------------------------------------------------------------- 1 | # Install Spinnaker 2 | 3 | Install Halyard and automatically configure Spinnaker to use Azure Storage (azs) as its persistent storage. 4 | 5 | ## Prerequisites 6 | This must be executed on a linux VM. You must run 'hal deploy apply' to finish deployment of Spinnaker. 7 | 8 | ## Arguments 9 | | Name | Description | 10 | |---|---| 11 | | --storage_account_name
-san | The storage account name used for Spinnaker's persistent storage service (front50). | 12 | | --storage_account_key
-sak | The storage account key used for Spinnaker's persistent storage service (front50). | 13 | | --username
-u | User for which to install Halyard. | 14 | 15 | ## Example usage 16 | ```bash 17 | ./install_spinnaker.sh --storage_account_name "sample" --storage_account_key "password" 18 | ``` 19 | 20 | ## Questions/Comments? azdevopspub@microsoft.com -------------------------------------------------------------------------------- /spinnaker/install_halyard/install_halyard.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function print_usage() { 4 | cat <&2 19 | print_usage 20 | exit -1 21 | fi 22 | } 23 | 24 | while [[ $# > 0 ]] 25 | do 26 | key="$1" 27 | shift 28 | case $key in 29 | --storage_account_name|-san) 30 | storage_account_name="$1";; 31 | --storage_account_key|-sak) 32 | storage_account_key="$1";; 33 | --username|-u) 34 | username="$1";; 35 | --help|-help|-h) 36 | print_usage 37 | exit 13;; 38 | *) 39 | echo "ERROR: Unknown argument '$key' to script '$0'" 1>&2 40 | exit -1 41 | esac 42 | shift 43 | done 44 | 45 | throw_if_empty storage_account_name $storage_account_name 46 | throw_if_empty storage_account_key $storage_account_key 47 | throw_if_empty username $username 48 | 49 | # Install Halyard 50 | curl --silent "https://raw.githubusercontent.com/spinnaker/halyard/master/install/debian/InstallHalyard.sh" | sudo bash -s -- --user "$username" -y 51 | 52 | # Get the latest version of spinnaker 53 | version=`hal version list | grep -P '\d+\.\d+\.\d+' -o | tail -1` 54 | 55 | # Set Halyard to use the latest released/validated version of Spinnaker 56 | hal config version edit --version $version 57 | 58 | # Configure Spinnaker persistent store 59 | hal config storage azs edit --storage-account-name "$storage_account_name" --storage-account-key "$storage_account_key" 60 | hal config storage edit --type azs --------------------------------------------------------------------------------