diff --git a/.gitignore b/.gitignore index adc35045..9cbb088f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.tfstate* *.tfvars *.tfplan +*.ovpn packer_cache packer/build packer/*.pem @@ -17,3 +18,4 @@ inventory/terraform.py tests/spec/*/*runtime_spec.rb contrib-plugins/* terraform/*/.terraform +terraform/azure/ssh_thumbprint diff --git a/bootstrap/azure/config-default.sh b/bootstrap/azure/config-default.sh index 4faa957e..16ee676a 100644 --- a/bootstrap/azure/config-default.sh +++ b/bootstrap/azure/config-default.sh @@ -5,8 +5,11 @@ export ATLAS_TOKEN=${ATLAS_TOKEN:?"Need to set ATLAS_TOKEN non-empty"} export ATLAS_INFRASTRUCTURE=${ATLAS_INFRASTRUCTURE:-capgemini/apollo} export TF_VAR_azure_settings_file=${TF_VAR_azure_settings_file:?"Need to set TF_VAR_azure_settings_file non-empty"} +export TF_VAR_user=${TF_VAR_user:?"Need to set User non-empty"} export TF_VAR_username=${TF_VAR_username:?"Need to set TF_VAR_username non-empty"} -export TF_VAR_ssh_key_thumbprint=${TF_VAR_ssh_key_thumbprint:?"Need to set TF_VAR_ssh_key_thumbprint non-empty"} + +export TF_VAR_key_file=${TF_VAR_key_file:-$HOME/.ssh/apollo_azure_rsa.pfx} +export TF_VAR_key_name=${TF_VAR_key_name:-apollo} # Overrides default folder in Terraform.py inventory. export TF_VAR_STATE_ROOT="${APOLLO_ROOT}/terraform/${APOLLO_PROVIDER}" @@ -15,8 +18,8 @@ export ANSIBLE_SSH_ARGS="-F ${APOLLO_ROOT}/terraform/${APOLLO_PROVIDER}/ssh.conf # Terraform mappings needs to be statically passed as -var parameters # so no really needed to export them. Exporting for consitency. -export TF_VAR_atlas_artifact_master=${TF_VAR_atlas_artifact_master:-capgemini/apollo-ubuntu-14.04-amd64} -export TF_VAR_atlas_artifact_slave=${TF_VAR_atlas_artifact_slave:-capgemini/apollo-ubuntu-14.04-amd64} +export TF_VAR_atlas_artifact_master=${TF_VAR_atlas_artifact_master:-apollo-ubuntu-14.04-amd64-1446651919} +export TF_VAR_atlas_artifact_slave=${TF_VAR_atlas_artifact_slave:-apollo-ubuntu-14.04-amd64-1446651919} export TF_VAR_atlas_artifact_version_master=${TF_VAR_atlas_artifact_version_master:-1} export TF_VAR_atlas_artifact_version_slave=${TF_VAR_atlas_artifact_version_slave:-1} @@ -25,5 +28,5 @@ export TF_VAR_master_size=${TF_VAR_master_size:-Medium} export TF_VAR_slave_size=${TF_VAR_slave_size:-Medium} export TF_VAR_slaves=${TF_VAR_slaves:-1} -export APOLLO_consul_dc=${APOLLO_consul_dc:-$TF_VAR_region} +export APOLLO_consul_dc=${APOLLO_consul_dc:-${TF_VAR_region// /_}} export APOLLO_mesos_cluster_name=${APOLLO_mesos_cluster_name:-$TF_VAR_region} diff --git a/bootstrap/azure/util.sh b/bootstrap/azure/util.sh index 18577541..3dff13c5 100644 --- a/bootstrap/azure/util.sh +++ b/bootstrap/azure/util.sh @@ -1,3 +1,59 @@ +#!/bin/bash + +# Use the config file specified in $APOLLO_CONFIG_FILE, or default to +# config-default.sh. +function set_vpn() { + while true; do + read -p "Do you want to start the VPN and setup a connection now (y/n)?" yn + case $yn in + [Yy]* ) ovpn_start;ovpn_client_config; break;; + [Nn]* ) exit;; + * ) echo "Please answer y or n.";; + esac + done +} + +ansible_ssh_config() { + pushd "${APOLLO_ROOT}/terraform/${APOLLO_PROVIDER}" + export APOLLO_bastion_ip=$( terraform output bastion.ip ) + + # Virtual private cloud CIDR IP. + ip=$( terraform output vn_cidr_block.ip ) + export APOLLO_network_identifier=$( get_network_identifier "${ip}" ) + + cat < ssh.config + Host bastion $APOLLO_bastion_ip + StrictHostKeyChecking no + User ubuntu + HostName $APOLLO_bastion_ip + ProxyCommand none + IdentityFile $TF_VAR_private_key_file + BatchMode yes + PasswordAuthentication no + UserKnownHostsFile /dev/null + + Host $APOLLO_network_identifier.* + StrictHostKeyChecking no + ServerAliveInterval 120 + TCPKeepAlive yes + ProxyCommand ssh -q -A -F $(pwd)/ssh.config ubuntu@$APOLLO_bastion_ip nc %h %p + ControlMaster auto + ControlPath ~/.ssh/mux-%r@%h:%p + ControlPersist 30m + User ubuntu + IdentityFile $TF_VAR_private_key_file + UserKnownHostsFile /dev/null +EOF + popd +} + +ssh_thumbprint() { + pushd "${APOLLO_ROOT}/terraform/${APOLLO_PROVIDER}" + if [ ! -f "ssh_thumbprint" ]; then + touch ssh_thumbprint + fi + popd +} apollo_down() { pushd "${APOLLO_ROOT}/terraform/${APOLLO_PROVIDER}" @@ -5,3 +61,36 @@ apollo_down() { -var "region=${TF_VAR_region}" popd } + +ovpn_start() { + pushd "${APOLLO_ROOT}/terraform/${APOLLO_PROVIDER}" + echo "... initialising VPN setup" >&2 + /bin/sh -x bin/ovpn-init + /bin/sh -x bin/ovpn-start + popd +} + +ovpn_client_config() { + pushd "${APOLLO_ROOT}/terraform/${APOLLO_PROVIDER}" + echo "... creating VPN client configuration" >&2 + /bin/sh -x bin/ovpn-new-client "${TF_VAR_user}" + /bin/sh -x bin/ovpn-client-config "${TF_VAR_user}" + + # We need to sed the .ovpn file to replace the correct IP address, because we are getting the + # instance IP address not the elastic IP address in the downloaded file. + bastion_ip=$(terraform output bastion.ip) + /usr/bin/env sed -i -e "s/\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}/${bastion_ip}/g" "${TF_VAR_user-apollo.ovpn}" + + /usr/bin/open "${TF_VAR_user-apollo.ovpn}" + # Display a prompt to tell the user to connect in their VPN client, + # and pause/wait for them to connect. + while true; do + read -p "Your VPN client should be open. Please now connect to the VPN using your VPN client. + Once connected hit y to open the web interface or n to exit (y/n)?" yn + case $yn in + [Yy]* ) popd;open_urls; break;; + [Nn]* ) popd;exit;; + * ) echo "Please answer y or n.";; + esac + done +} diff --git a/bootstrap/common.sh b/bootstrap/common.sh index 74ec06e0..1f4e5593 100644 --- a/bootstrap/common.sh +++ b/bootstrap/common.sh @@ -3,6 +3,8 @@ # Util functions cloud reusable. APOLLO_ROOT=$(dirname "${BASH_SOURCE}")/.. DEFAULT_CONFIG="${APOLLO_ROOT}/bootstrap/${APOLLO_PROVIDER}/${APOLLO_CONFIG_FILE-"config-default.sh"}" +DYNAMIC_INVENTORY="https://raw.githubusercontent.com/Capgemini/terraform.py/master/terraform.py" + if [ -f "${DEFAULT_CONFIG}" ]; then source "${DEFAULT_CONFIG}" fi @@ -77,6 +79,7 @@ apollo_launch() { elif [ "$@" ]; then eval $@ else + run_if_exist "ssh_thumbprint" get_terraform_modules terraform_apply run_if_exist "ansible_ssh_config" @@ -142,7 +145,7 @@ ansible_playbook_run() { get_ansible_inventory() { pushd $APOLLO_ROOT if [ ! -f inventory/terraform.py ]; then - curl -sS https://raw.githubusercontent.com/Capgemini/terraform.py/master/terraform.py -o inventory/terraform.py + curl -sS ${DYNAMIC_INVENTORY} -o inventory/terraform.py chmod 755 inventory/terraform.py fi popd diff --git a/docs/getting-started-guides/azure.md b/docs/getting-started-guides/azure.md index dc30e45a..0753ae6e 100644 --- a/docs/getting-started-guides/azure.md +++ b/docs/getting-started-guides/azure.md @@ -1,33 +1,121 @@ ## Getting started on Microsoft Azure -settings file - +### Prerequisites -https://manage.windowsazure.com/publishsettings +1. You need an Microsoft Azure account. Visit [http://azure.microsoft.com](http://azure.microsoft.com) to get started. +2. Unfortunately the latest version of terraform doesn't fully support azure so you need to compile terraform from the master branch to get it working. Visit [https://github.com/hashicorp/terraform](https://github.com/hashicorp/terraform) to get more instruction how to do it. +3. You need to have [Python](https://www.python.org/) >= 2.7.5 installed along with [pip](https://pip.pypa.io/en/latest/installing.html). +4. You need to install Azure CLI on your machine, you can find instructions how to do it [here](https://azure.microsoft.com/en-gb/documentation/articles/xplat-cli-install/). To find out more how to connect Azure subscription +from the Azure CLI you can find it [here](https://azure.microsoft.com/en-gb/documentation/articles/xplat-cli-connect/) +Download settings file +[https://manage.windowsazure.com/publishsettings](https://manage.windowsazure.com/publishsettings) +and perform the command: -Create SSH Key thumbprint - +``` +azure account import path_to_your_publishsettings_file +``` -https://azure.microsoft.com/en-gb/documentation/articles/virtual-machines-linux-use-ssh-key/ +Create an Azure Compatible Keys [more details you can find here](https://azure.microsoft.com/en-gb/documentation/articles/virtual-machines-linux-use-ssh-key/) -Upload SSH cert - +``` +openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ~/.ssh/id_rsa_azure.key -out ~/.ssh/id_rsa_azure.pem -subj '/CN=www.capgemini.com/O=Apollo./C=UK' -https://manage.windowsazure.com/#Workspaces/AdminTasks/ListManagementCertificates +openssl x509 -outform der -in ~/.ssh/id_rsa_azure.pem -out ~/.ssh/id_rsa_azure.pfx -Create a storage account - +ssh-add id_rsa_azure.key +``` -https://manage.windowsazure.com/#Workspaces/StorageExtension +Upload SSH cert [https://manage.windowsazure.com/#Workspaces/AdminTasks/ListManagementCertificates](https://manage.windowsazure.com/#Workspaces/AdminTasks/ListManagementCertificates) -I named it capgeminiapollo +Create a storage account [https://manage.windowsazure.com/#Workspaces/StorageExtension](https://manage.windowsazure.com/#Workspaces/StorageExtension). I named it capgeminiapollo -Create a storage container - - -https://manage.windowsazure.com/#Workspaces/StorageExtension/StorageAccount/capgeminiapollo/Containers +Create a storage container [https://manage.windowsazure.com/#Workspaces/StorageExtension/StorageAccount/capgeminiapollo/Containers](https://manage.windowsazure.com/#Workspaces/StorageExtension/StorageAccount/capgeminiapollo/Containers) Use the storage account name from above here and named the storage container 'images'. -Packer build - +### Cluster Turnup + +#### Download Apollo + +##### Install from source at head +1. `git clone https://github.com/Capgemini/apollo.git` +2. `cd apollo` +3. `pip install -r requirements.txt` + +#### Set config + +Configuration can be set via environment variables. + +All variables following the pattern "TF_VAR_" will be available for Apollo in terraform, see [https://github.com/hashicorp/terraform/pull/1621#issuecomment-100825568](https://github.com/hashicorp/terraform/pull/1621#issuecomment-100825568) + +All variables following pattern "APOLLO_" will be available for Apollo in ansible. + +For a full list of default config options for AWS see `bootstrap/azure/config-default.sh` + +As a minimum you will need to set these environment variables - + +``` +export APOLLO_PROVIDER=azure +export TF_VAR_user=$USER +export TF_VAR_azure_settings_file='path_to_your_azure_setting_file' +export TF_VAR_username='ubuntu' +export ATLAS_TOKEN=your_atlas_token +export TF_VAR_ssh_key_thumbprint=~/.ssh/id_rsa_azure.pem +export TF_VAR_private_key_file=~/.ssh/id_rsa_azure.key +``` + +_NOTE: The value for ATLAS_TOKEN should be set to whatever you generated with your [Atlas Account](https://atlas.hashicorp.com/settings/tokens). + +#### Turn up the cluster +``` +/bin/bash bootstrap/apollo-launch.sh +``` + +NOTE: The script will provision a new VPC and a 3 node mesos master cluster for North Europe region. + +It will also create a mesos slave cluster and a Bastion server for accessing the VPC via VPN and SSH. + +It will then generate a local SSH config for the Bastion server and the private instances, and run an Ansible playbook to provision the cluster. + +Finally it will attempt to start and configure a VPN client connection for you. + +For instructions on how to configure the VPN (outside of the bootstrap script) to access the web interface of the tools see the [vpn guide](https://github.com/ravbaba/Apollo/blob/azure-provider/docs/getting-started-guides/aws/vpn.md). + +#### Tearing down the cluster +``` +/bin/bash bootstrap/apollo-down.sh +``` + +### Packer build + +Follow the instructions [here](https://github.com/msopentech/packer-azure) to build the compile the packer plugin for Azure via Go and copy it to your packer install folder (normally +/usr/local/packer/) and set those variables: + +``` +export AZURE_SETTINGS_FILE=path_to_your_azure_publishsettings_file +export AZURE_SUBSCRIPTION_NAME='Free Trial' +export AZURE_STORAGE_ACCOUNT='capgeminiapollo' +export AZURE_STORAGE_CONTAINER='images' +export AZURE_LOCATION='North Europe' +export AZURE_SOURCE_IMAGE='Ubuntu Server 14.04.3-LTS' +export AZURE_INSTANCE_TYPE='Small' +export APOLLO_VERSION=0.2.0 +``` + +in packer folder run the below command: + +``` +packer build ubuntu-14.04_amd64-azure.json +``` + +#### SSH'ing to your private instances + +As part of `apollo-launch.sh` an SSH config file will be generated in `$APOLLO_ROOT/terraform/azure/ssh.config`. + +If your master instance has a private IP of 10.0.1.11 (for example), You can SSH directly to that instance by doing the following - -Follow the instructions here https://github.com/msopentech/packer-azure to build the compile -the packer plugin for Azure via Go and copy it to your packer install folder (normally -/usr/local/packer/) and then - +``` +ssh -F $APOLLO_ROOT/terraform/azure/ssh.config 10.0.1.11 +``` -packer build -var 'azure_settings_file=azure.publishsettings' -var 'azure_subscription_name=Free Trial' -var 'azure_storage_account=capgeminiapollo' -var 'azure_storage_container=images' -var 'azure_location=North Europe' -var 'azure_source_image=Ubuntu Server 14.04 LTS' -var 'azure_instance_type=Small' -var 'version=1.0.0' ubuntu-14.04_amd64-azure.json +This will proxy your SSH command via the Bastion server and you should land in the master private instance. diff --git a/packer/scripts/common/sshd.sh b/packer/scripts/common/sshd.sh index 5a1e372e..3d0f2611 100644 --- a/packer/scripts/common/sshd.sh +++ b/packer/scripts/common/sshd.sh @@ -3,4 +3,4 @@ set -eu set -o pipefail # UseDNS is mostly useless and disabling speeds up logins -echo "UseDNS no" >> /etc/ssh/sshd_config +sed -i '$a UseDNS no' /etc/ssh/sshd_config diff --git a/packer/ubuntu-14.04_amd64-azure.json b/packer/ubuntu-14.04_amd64-azure.json index a9e67891..53f59cdd 100644 --- a/packer/ubuntu-14.04_amd64-azure.json +++ b/packer/ubuntu-14.04_amd64-azure.json @@ -7,10 +7,11 @@ "azure_location": "{{env `AZURE_LOCATION`}}", "azure_source_image": "{{env `AZURE_SOURCE_IMAGE`}}", "azure_instance_type": "{{env `AZURE_INSTANCE_TYPE`}}", - "mesos_version": "0.22.1-1.0.ubuntu1404", - "marathon_version": "0.8.1-1.0.171.ubuntu1404", - "consul_version": "0.5.1", - "weave_version": "v0.11.2", + "mesos_version": "0.23.0-1.0.ubuntu1404", + "marathon_version": "v0.10.1", + "consul_version": "0.5.2", + "weave_version": "1.2.0", + "docker_version": "1.9.0-0~trusty", "version": "{{env `APOLLO_VERSION`}}" }, "builders": [{ @@ -31,47 +32,29 @@ "source": "scripts/ubuntu/upstart/", "destination": "/tmp" }, + { + "type": "file", + "source": "tests", + "destination": "/tmp" + }, { "type": "shell", "environment_vars": [ "CONSUL_VERSION={{user `consul_version`}}", "WEAVE_VERSION={{user `weave_version`}}", "MESOS_VERSION={{user `mesos_version`}}", - "MARATHON_VERSION={{user `marathon_version`}}" + "MARATHON_VERSION={{user `marathon_version`}}", + "DOCKER_VERSION={{user `docker_version`}}" ], "scripts": [ "scripts/ubuntu/base.sh", "scripts/common/sshd.sh", "scripts/ubuntu/install_docker.sh", - "scripts/ubuntu/install_mesos.sh", - "scripts/ubuntu/install_marathon.sh", "scripts/common/install_consul.sh", - "scripts/ubuntu/install_dnsmasq.sh", - "scripts/common/install_weave.sh" + "scripts/common/install_weave.sh", + "scripts/common/serverspec.sh" ], "execute_command": "{{ .Vars }} sudo -E -S bash -c '{{ .Path }}'" - }, - { - "type": "file", - "source": "tests", - "destination": "/tmp" - }, - { - "type": "shell", - "script": "scripts/common/serverspec.sh", - "execute_command": "{{ .Vars }} sudo -E -S bash -c '{{ .Path }}'" - } - ], - "push": { - "name": "capgemini/apollo" - }, - "post-processors": [{ - "type": "atlas", - "artifact": "capgemini/apollo-ubuntu-14.04-amd64", - "artifact_type": "azure.image", - "metadata": { - "created_at": "{{timestamp}}", - "version": "{{user `version`}}+{{timestamp}}" } - }] + ] } diff --git a/site.yml b/site.yml index 7d0e02ee..46c83964 100644 --- a/site.yml +++ b/site.yml @@ -7,12 +7,12 @@ port: "{{ ansible_ssh_port }}" host: "{{ ansible_ssh_host }}" delay: 10 - timeout: 60 + timeout: 120 delegate_to: "{{ bastion_ip }}" sudo: False when: bastion_ip is defined - name: Wait for port 22 to become available from local server. - local_action: "wait_for port={{ ansible_ssh_port }} host={{ ansible_ssh_host }} delay=10 timeout=60" + local_action: "wait_for port={{ ansible_ssh_port }} host={{ ansible_ssh_host }} delay=10 timeout=120" sudo: False when: bastion_ip is not defined diff --git a/terraform/azure/azure-upload-certificate.sh b/terraform/azure/azure-upload-certificate.sh new file mode 100755 index 00000000..e02482df --- /dev/null +++ b/terraform/azure/azure-upload-certificate.sh @@ -0,0 +1,10 @@ +#!/bin/sh +set -e + +azure config mode asm + +azure service cert create $1 ~/.ssh/id_rsa_azure.pfx + +azure service cert list | \ + grep $1 | \ + awk '{print $3}' | tr -d '\n' > ssh_thumbprint diff --git a/terraform/azure/bastion-server.tf b/terraform/azure/bastion-server.tf new file mode 100644 index 00000000..a71ee386 --- /dev/null +++ b/terraform/azure/bastion-server.tf @@ -0,0 +1,75 @@ +resource "azure_hosted_service" "bastion-service" { + name = "bastion-server" + location = "${var.region}" + ephemeral_contents = false + description = "Mesos master service" + label = "bastion-service" + provisioner "local-exec" { + command = "./azure-upload-certificate.sh bastion-server" + } +} + +/* NAT/VPN server */ +resource "azure_instance" "bastion" { + name = "apollo-bastion" + hosted_service_name = "${azure_hosted_service.bastion-service.name}" + description = "bastion" + image = "Ubuntu Server 14.04 LTS" + size = "${var.instance_type.master}" + storage_service_name = "${azure_storage_service.azure_mesos_storage.name}" + security_group = "${azure_security_group.bastion.name}" + virtual_network = "${azure_virtual_network.virtual-network.id}" + subnet = "public" + location = "${var.region}" + username = "${var.username}" + ssh_key_thumbprint = "${file("ssh_thumbprint")}" + + endpoint { + name = "SSH" + protocol = "tcp" + public_port = 22 + private_port = 22 + } + + endpoint { + name = "OpenVPN" + protocol = "udp" + public_port = 1194 + private_port = 1194 + } + + endpoint { + name = "HTTPS" + protocol = "tcp" + public_port = 443 + private_port = 443 + } + + endpoint { + name = "HTTP" + protocol = "tcp" + public_port = 80 + private_port = 80 + } + + provisioner "remote-exec" { + inline = [ + "sudo iptables -t nat -A POSTROUTING -j MASQUERADE", + "echo 1 | sudo tee /proc/sys/net/ipv4/conf/all/forwarding", + /* Install docker */ + /* Add the repository to your APT sources */ + "sudo -E sh -c 'echo deb https://apt.dockerproject.org/repo ubuntu-trusty main > /etc/apt/sources.list.d/docker.list'", + /* Then import the repository key */ + "sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D", + "sudo apt-get update", + /* Install docker-engine */ + "sudo apt-get install -y docker-engine=${var.docker_version}", + "sudo service docker start", + /* Initialize open vpn data container */ + "sudo mkdir -p /etc/openvpn", + "sudo docker run --name ovpn-data -v /etc/openvpn busybox", + /* Generate OpenVPN server config */ + "sudo docker run --volumes-from ovpn-data --rm gosuri/openvpn ovpn_genconfig -p ${var.vn_cidr_block} -u udp://${azure_instance.bastion.vip_address}" + ] + } +} diff --git a/terraform/azure/bin/ovpn-client-config b/terraform/azure/bin/ovpn-client-config new file mode 100755 index 00000000..7c6adc20 --- /dev/null +++ b/terraform/azure/bin/ovpn-client-config @@ -0,0 +1 @@ +ssh -t "ubuntu@$(terraform output bastion.ip)" sudo docker run --volumes-from ovpn-data --rm gosuri/openvpn ovpn_getclient "${1}" > "${1}-apollo.ovpn" diff --git a/terraform/azure/bin/ovpn-init b/terraform/azure/bin/ovpn-init new file mode 100755 index 00000000..f8acfcd4 --- /dev/null +++ b/terraform/azure/bin/ovpn-init @@ -0,0 +1 @@ +ssh -t "ubuntu@$(terraform output bastion.ip)" sudo docker run --volumes-from ovpn-data --rm -it gosuri/openvpn ovpn_initpki diff --git a/terraform/azure/bin/ovpn-new-client b/terraform/azure/bin/ovpn-new-client new file mode 100755 index 00000000..9225972f --- /dev/null +++ b/terraform/azure/bin/ovpn-new-client @@ -0,0 +1 @@ +ssh -t "ubuntu@$(terraform output bastion.ip)" sudo docker run --volumes-from ovpn-data --rm -it gosuri/openvpn easyrsa build-client-full "${1}" nopass diff --git a/terraform/azure/bin/ovpn-start b/terraform/azure/bin/ovpn-start new file mode 100755 index 00000000..8ef9b06a --- /dev/null +++ b/terraform/azure/bin/ovpn-start @@ -0,0 +1 @@ +ssh -t "ubuntu@$(terraform output bastion.ip)" sudo docker run --volumes-from ovpn-data -d -p 1194:1194/udp --cap-add=NET_ADMIN gosuri/openvpn diff --git a/terraform/azure/mesos-masters.tf b/terraform/azure/mesos-masters.tf index 74987d83..50668263 100644 --- a/terraform/azure/mesos-masters.tf +++ b/terraform/azure/mesos-masters.tf @@ -1,21 +1,29 @@ +/* Cloud services for master instances */ +resource "azure_hosted_service" "mesos-master" { + name = "${var.hosted_service_name.master}-${count.index}" + count = "${var.masters}" + location = "${var.region}" + ephemeral_contents = false + description = "Mesos master service ${count.index}" + label = "mesos_masters" + provisioner "local-exec" { + command = "./azure-upload-certificate.sh ${var.hosted_service_name.master}-${count.index}" + } +} + /* Mesos master instances */ resource "azure_instance" "mesos-master" { name = "apollo-mesos-master-${count.index}" - description = "Mesos master ${count.index}" + hosted_service_name = "${element(azure_hosted_service.mesos-master.*.name, count.index)}" + depends_on = ["azure_instance.bastion"] + description = "mesos_masters" count = "${var.masters}" - /* @todo - replace with variable */ - image = "apollo-ubuntu-14.04-amd64-1434963352" + image = "${var.atlas_artifact.master}" size = "${var.instance_type.master}" - security_group = "${azure_security_group.default.name}" + storage_service_name = "${azure_storage_service.azure_mesos_storage.name}" + virtual_network = "${azure_virtual_network.virtual-network.id}" + subnet = "private" location = "${var.region}" username = "${var.username}" - ssh_key_thumbprint = "${var.ssh_key_thumbprint}" - /*user_data = "{role: mesos_masters}"*/ - - endpoint { - name = "SSH" - protocol = "tcp" - public_port = 22 - private_port = 22 - } + ssh_key_thumbprint = "${file("ssh_thumbprint")}" } diff --git a/terraform/azure/mesos-slaves.tf b/terraform/azure/mesos-slaves.tf index c90d92c2..30eb0f8e 100644 --- a/terraform/azure/mesos-slaves.tf +++ b/terraform/azure/mesos-slaves.tf @@ -1,22 +1,29 @@ +/* Cloud services for slave instances */ +resource "azure_hosted_service" "mesos-slave" { + name = "${var.hosted_service_name.slave}-${count.index}" + count = "${var.slaves}" + location = "${var.region}" + ephemeral_contents = false + description = "Mesos slave service ${count.index}" + label = "mesos_slaves" + provisioner "local-exec" { + command = "./azure-upload-certificate.sh ${var.hosted_service_name.slave}-${count.index}" + } +} + /* Mesos slave instances */ resource "azure_instance" "mesos-slave" { name = "apollo-mesos-slave-${count.index}" - description = "Mesos slave ${count.index}" - depends_on = ["azure_instance.mesos-master"] + hosted_service_name = "${element(azure_hosted_service.mesos-slave.*.name, count.index)}" + depends_on = ["azure_instance.bastion", "azure_instance.mesos-master"] + description = "mesos_slaves" count = "${var.slaves}" - /* @todo - replace with variable or atlas artifact */ - image = "apollo-ubuntu-14.04-amd64-1434963352" + image = "${var.atlas_artifact.slave}" size = "${var.instance_type.slave}" - security_group = "${azure_security_group.default.name}" + storage_service_name = "${azure_storage_service.azure_mesos_storage.name}" + virtual_network = "${azure_virtual_network.virtual-network.id}" + subnet = "private" location = "${var.region}" username = "${var.username}" - ssh_key_thumbprint = "${var.ssh_key_thumbprint}" - /*user_data = "{role: mesos_slaves}"*/ - - endpoint { - name = "SSH" - protocol = "tcp" - public_port = 22 - private_port = 22 - } + ssh_key_thumbprint = "${file("ssh_thumbprint")}" } diff --git a/terraform/azure/outputs.tf b/terraform/azure/outputs.tf index c975c266..c28bbbf4 100644 --- a/terraform/azure/outputs.tf +++ b/terraform/azure/outputs.tf @@ -1,9 +1,17 @@ +output "vn_cidr_block.ip" { + value = "${var.vn_cidr_block}" +} +/* bastion address */ +output "bastion.ip" { + value = "${azure_instance.bastion.vip_address}" +} +/* Private addresseses */ output "master.1.ip" { - value = "${azure_instance.mesos-master.0.vip_address}" + value = "${azure_instance.mesos-master.0.ip_address}" } output "master_ips" { - value = "${join(",", azure_instance.mesos-master.*.vip_address)}" + value = "${join(",", azure_instance.mesos-master.*.ip_address)}" } output "slave_ips" { - value = "${join(",", azure_instance.mesos-slave.*.vip_address)}" + value = "${join(",", azure_instance.mesos-slave.*.ip_address)}" } diff --git a/terraform/azure/provider.tf b/terraform/azure/provider.tf index 0086ebff..66bf9620 100644 --- a/terraform/azure/provider.tf +++ b/terraform/azure/provider.tf @@ -1,3 +1,8 @@ +/* + You need to download the setting file from + https://manage.windowsazure.com/publishsettings and set env variable + TF_VAR_azure_settings_file pointing to the file. +*/ provider "azure" { - settings_file = "${var.azure_settings_file}" + settings_file = "${file(var.azure_settings_file)}" } diff --git a/terraform/azure/security-groups.tf b/terraform/azure/security-groups.tf index f2298702..4ab4c020 100644 --- a/terraform/azure/security-groups.tf +++ b/terraform/azure/security-groups.tf @@ -1,31 +1,63 @@ -/* Default security group */ -resource "azure_security_group" "default" { - name = "default-apollo-mesos" +/* Security group for the bastion server */ +resource "azure_security_group" "bastion" { + name = "bastion-apollo-mesos" location = "${var.region}" } -resource "azure_security_group_rule" "all-inbound" { - name = "all-inbound-access-rule" - security_group_name = "${azure_security_group.default.name}" - type = "Inbound" - action = "Allow" - priority = 100 - source_address_prefix = "*" - source_port_range = "*" - destination_address_prefix = "*" - destination_port_range = "*" - protocol = "TCP" +/* Security group for web traffic */ +resource "azure_security_group" "web" { + name = "web-apollo-mesos" + location = "${var.region}" +} + +resource "azure_security_group_rule" "public_ssh_access" { + name = "ssh-access-rule" + security_group_names = ["${azure_security_group.bastion.name}"] + type = "Inbound" + action = "Allow" + priority = 200 + source_address_prefix = "*" + source_port_range = "*" + destination_address_prefix = "${var.public_subnet_cidr_block}" + destination_port_range = "22" + protocol = "TCP" +} + +resource "azure_security_group_rule" "open_vpn" { + name = "open-vpn-rule" + security_group_names = ["${azure_security_group.bastion.name}"] + type = "Inbound" + action = "Allow" + priority = 201 + source_address_prefix = "*" + source_port_range = "*" + destination_address_prefix = "${var.public_subnet_cidr_block}" + destination_port_range = "1194" + protocol = "UDP" +} + +resource "azure_security_group_rule" "https" { + name = "https-rule" + security_group_names = ["${azure_security_group.bastion.name}"] + type = "Outbound" + action = "Allow" + priority = 202 + source_address_prefix = "*" + source_port_range = "*" + destination_address_prefix = "${var.public_subnet_cidr_block}" + destination_port_range = "443" + protocol = "TCP" } -resource "azure_security_group_rule" "all-outbound" { - name = "all-outbound-access-rule" - security_group_name = "${azure_security_group.default.name}" - type = "Outbound" - action = "Allow" - priority = 100 - source_address_prefix = "*" - source_port_range = "*" +resource "azure_security_group_rule" "http" { + name = "http-rule" + security_group_names = ["${azure_security_group.bastion.name}", "${azure_security_group.web.name}"] + type = "Outbound" + action = "Allow" + priority = 203 + source_address_prefix = "*" + source_port_range = "*" destination_address_prefix = "*" - destination_port_range = "*" - protocol = "TCP" + destination_port_range = "80" + protocol = "TCP" } diff --git a/terraform/azure/storage.tf b/terraform/azure/storage.tf new file mode 100644 index 00000000..b505e92a --- /dev/null +++ b/terraform/azure/storage.tf @@ -0,0 +1,6 @@ +resource "azure_storage_service" "azure_mesos_storage" { + name = "${var.storage_name}" + location = "${var.region}" + description = "Made by Terraform." + account_type = "Standard_GRS" +} diff --git a/terraform/azure/variables.tf b/terraform/azure/variables.tf index 30bc6e4c..ea631534 100644 --- a/terraform/azure/variables.tf +++ b/terraform/azure/variables.tf @@ -18,6 +18,21 @@ variable "region" { default = "North Europe" } +variable "vn_cidr_block" { + description = "Cidr block for the VN." + default = "10.0.0.0/16" +} + +variable "private_subnet_cidr_block" { + description = "CIDR for public subnet" + default = "10.0.0.0/24" +} + +variable "public_subnet_cidr_block" { + description = "CIDR for public subnet" + default = "10.0.1.0/24" +} + variable "slaves" { description = "The number of slaves." default = "1" @@ -34,3 +49,27 @@ variable "instance_type" { slave = "Medium" } } + +variable "atlas_artifact" { + default = { + master = "apollo-ubuntu-14.04-amd64-1446651919" + slave = "apollo-ubuntu-14.04-amd64-1446651919" + } +} + +variable "hosted_service_name" { + default = { + master = "apollo-mesos-master" + slave = "apollo-mesos-slave" + } +} + +variable "storage_name" { + description = "Storage name" + default = "mesosimages" +} + +variable "docker_version" { + description = "Docker version" + default = "1.9.0-0~trusty" +} diff --git a/terraform/azure/virtual-network.tf b/terraform/azure/virtual-network.tf new file mode 100644 index 00000000..e59f8eed --- /dev/null +++ b/terraform/azure/virtual-network.tf @@ -0,0 +1,15 @@ +resource "azure_virtual_network" "virtual-network" { + name = "virtual-network" + address_space = ["${var.vn_cidr_block}"] + location = "${var.region}" + + subnet { + name = "private" + address_prefix = "${var.private_subnet_cidr_block}" + } + + subnet { + name = "public" + address_prefix = "${var.public_subnet_cidr_block}" + } +}