diff --git a/.gitignore b/.gitignore index 83d4bfd..ff823da 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ gce/ssh/ aws/ssh/ gce/account.json +azure/credentials.publishsettings *.tfstate *.tfstate.backup +azure/ssh_thumbprint +generated.* diff --git a/Makefile b/Makefile index 2b14099..3b191a8 100644 --- a/Makefile +++ b/Makefile @@ -12,9 +12,28 @@ set-aws: $(eval dir=aws) set-gce: $(eval dir=gce) +set-azure: + $(eval dir=azure) aws: set-aws apply provision gce: set-gce apply provision +azure: set-azure prepare-azure apply-azure provision + +prepare-azure: set-azure + @cd ${dir} && \ + ./azure-generate-account-settings.sh ${DEPLOY_ENV} + # Files generated by external scripts and read by terraform + @cd ${dir} && \ + touch \ + generated.cf-storage-account.key \ + generated.insecure-deployer.pem \ + generated.bosh-public-ip \ + generated.ssh_thumbprint + +apply-azure: set-azure + @cd ${dir} && \ + source generated.azure_account_settings.sh && \ + terraform apply -state=${DEPLOY_ENV}.tfstate -var env=${DEPLOY_ENV} apply-aws: set-aws apply apply-gce: set-gce apply @@ -23,25 +42,33 @@ apply: check-env-vars provision-aws: set-aws provision provision-gce: set-gce provision +provision-azure: set-azure provision provision: check-env-vars @ssh -oStrictHostKeyChecking=no ubuntu@$(shell terraform output -state=${dir}/${DEPLOY_ENV}.tfstate bastion_ip) '/bin/bash provision.sh' bosh-delete-aws: set-aws bosh-delete bosh-delete-gce: set-gce bosh-delete +bosh-delete-azure: set-azure bosh-delete bosh-delete: @ssh -oStrictHostKeyChecking=no ubuntu@$(shell terraform output -state=${dir}/${DEPLOY_ENV}.tfstate bastion_ip) './`ls bosh-init-*` delete manifest_${dir}.yml' +azure-clean: + @cd ${dir} && ./azure-delete-environment.sh ${DEPLOY_ENV} + destroy-aws: set-aws destroy destroy-gce: set-gce destroy +destroy-azure: set-azure azure-clean destroy destroy: @cd ${dir} && terraform destroy -state=${DEPLOY_ENV}.tfstate -var env=${DEPLOY_ENV} show-aws: set-aws show show-gce: set-gce show +show-azure: set-azure show show: @cd ${dir} && terraform show ${DEPLOY_ENV}.tfstate ssh-aws: set-aws ssh ssh-gce: set-gce ssh +ssh-azure: set-azure ssh ssh: check-env-vars @ssh -oStrictHostKeyChecking=no ubuntu@$(shell terraform output -state=${dir}/${DEPLOY_ENV}.tfstate bastion_ip) diff --git a/README.md b/README.md index 2e621f8..9028783 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ To provision a microbosh instance on AWS and GCE. In order to deploy a microbosh, it is necessary to first create subnets, security groups and static IP reservations which will be used by bosh-init when deploying the microbosh. We are using terraform to create these resources, along with a bastion host which will perform the actual `bosh-init` steps to create the microbosh. -##Pre-requisites +## Pre-requisites * You will need to be running ssh-agent and have performed an `ssh-add ` to make the credentials available for ssh to be able to connect into the bastion host * Make available the ssh directory inside aws and gce @@ -18,7 +18,36 @@ gce/ insecure-deployer insecure-deployer.pub ``` -* Provide `account.json` inside gce + +### GCE pre-requisites + +* Provide `account.json` inside gce, which must be downloaded from your google + compute dashboard. + +### Azure pre-requisites + +Tooling: + + * You need to [install azure client](https://azure.microsoft.com/en-gb/documentation/articles/xplat-cli-install/) to be able to upload the SSH credentials (if you have [brew cask](http://caskroom.io/) `brew cask install azure`) + * You must login in azure client]: `azure login` + +> Note: it is recommended run `azure account clear` first to remove any previous accounts. + +Credentials: + + * Download the azure credentials in `azure/credentials.publishsettings` manually [from here https://manage.windowsazure.com/publishsettings] (the `azure account download` just sends you to this page). + +Restrictions: + + * Your environment name must not contain special chars, only alphanumeric in lower case. This is because a restriction in the storage service resource: + ``` +* azure_storage_service.cf-storage: Failed to create Azure storage service hectorjimazure-cf-storage: Error response from Azure. Code: BadRequest, Message: The name is not a valid storage account name. Storage account names must be between 3 and 24 characters in length and use numbers and lower-case letters only. +``` + * Do not login in azure using `azure import `. There is a bug and not all the values might be initialised. + + +### AWS pre-requisites + * Provide AWS access keys as environment variables, plus the corresponding terraform variables. Example in profile: ``` diff --git a/aws/ssh b/aws/ssh new file mode 120000 index 0000000..af60fad --- /dev/null +++ b/aws/ssh @@ -0,0 +1 @@ +../ssh \ No newline at end of file diff --git a/azure/azure-acl-rule.sh b/azure/azure-acl-rule.sh new file mode 100755 index 0000000..c6b61b0 --- /dev/null +++ b/azure/azure-acl-rule.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +set -e + +SCRIPT_NAME=$0 + +usage() { + cat < + +Creates the given ACL rules in azure using 'azure-cli'. It implements logic to split +the cidr by comma and add multiple rules. + +Note: base priority will be incremented as many CIDR addresses are given. + +EOF +} + +if [ "$#" -lt 5 ]; then + usage + exit 1 +fi + +host=$1; shift +endpoint=$1; shift +priority=$1; shift +action=$1; shift +list_cidr=$1; shift + +IFS=',' +count=0 +for cidr in $list_cidr; do + echo "Executing 'azure vm endpoint acl-rule create $host $endpoint $((priority+count)) $action $cidr'" + azure vm endpoint acl-rule create $host $endpoint $((priority+count)) $action $cidr + count=$((count + 1)) +done + +azure vm endpoint acl-rule list $host $endpoint diff --git a/azure/azure-create-network.sh b/azure/azure-create-network.sh new file mode 100755 index 0000000..b944507 --- /dev/null +++ b/azure/azure-create-network.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +if [ $# -lt 5 ]; then + cat < + +Creates network and subnet for cloudfoundry + +Can be deleted with: "azure network vnet delete env-cf-hosted-service env-cf-network" +EOF + exit 1 +fi + +resource_group_name=$1; shift +network_name=$1; shift +network_cidr=$1; shift +subnet_name=$1; shift +subnet_cidr=$1; shift + +azure config mode arm +azure resource create \ + ${resource_group_name} \ + ${network_name} \ + Microsoft.Network/virtualNetworks \ + 'West Europe' 2015-05-01-preview \ + -p "{\"addressSpace\": {\"addressPrefixes\": [\"${network_cidr}\"]},\"subnets\": [{\"name\": \"${subnet_name}\",\"properties\" : { \"addressPrefix\": \"${subnet_cidr}\"}}]}" + diff --git a/azure/azure-create-public-ip.sh b/azure/azure-create-public-ip.sh new file mode 100755 index 0000000..fe6a13d --- /dev/null +++ b/azure/azure-create-public-ip.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +set -e + +if [ $# -lt 2 ]; then + cat < + +Creates a public IP +EOF + exit 1 +fi + +resource_group_name=$1; shift +ip_name=$1; shift + +azure config mode arm +azure resource create \ + ${resource_group_name} \ + ${ip_name} \ + Microsoft.Network/publicIPAddresses \ + "West Europe" 2015-05-01-preview \ + -p "{\"publicIPAllocationMethod\":\"static\"}" + +sleep 5 + +azure resource show \ + ${resource_group_name} \ + ${ip_name} \ + Microsoft.Network/publicIPAddresses \ + 2015-05-01-preview | awk '/Property ipAddress/ {print $4}' | tee generated.${ip_name} + +echo "Created IP $( + +Setups the application and service pricipal for bosh for the given environment. +Granting the role of Contributor. + +Password must be pass as a environment variable: \$SERVICE_PASSWORD +EOF + exit 1 +fi + +deploy_env=$1; shift +azure_subscription_id=$1; shift + +azure config mode arm + +echo "Creating application resource in the active directory:" +azure ad app create \ + --name "Service Principal for BOSH - Environment: $deploy_env" \ + --password "$SERVICE_PASSWORD" \ + --home-page "http://BOSHAzureCPI-$deploy_env" \ + --identifier-uris "http://BOSHAzureCPI-$deploy_env" | \ + tee generated.active_directory_app_${deploy_env}_info.txt + +if [ ${PIPESTATUS[0]} != 0 ]; then + # Skip if it already exists + if grep -q 'Another object with the same value for property identifierUris already exists' ~/.azure/azure.err; then + echo "The app already exists, skipping" + else + echo "Failed" + exit 1 + fi +fi + +application_id=$(cat generated.active_directory_app_${deploy_env}_info.txt | sed -n 's/.*Application Id: *\(.*\)$/\1/p') +echo $application_id > generated.application_id +echo "Created application 'http://BOSHAzureCPI-$deploy_env' with ID (this is your client_id): $application_id" + +echo "Creating Service principal for the created application $application_id" +azure ad sp create $application_id || exit 1 + +echo "Waiting for the orcs in Azure to manually create the resources we have just requested..." +sleep 30 + +echo "Assigning 'Contributor' role to application 'http://BOSHAzureCPI-$deploy_env'" +azure role assignment create \ + --spn "http://BOSHAzureCPI-$deploy_env" \ + -o "Contributor" \ + --subscription $azure_subscription_id || exit 1 + + diff --git a/azure/azure-create-storage-service.sh b/azure/azure-create-storage-service.sh new file mode 100755 index 0000000..f8a3a2c --- /dev/null +++ b/azure/azure-create-storage-service.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +if [ $# -lt 3 ]; then + cat < + +Will create a storage service name and store the credencials on the given file +so they can be consumed by terraform +EOF + exit 1 +fi + +resource_group_name=$1; shift +storage_account_name=$1; shift +account_key_file=$1; shift + +set -x + +azure config mode arm # Change azure client mode, needed to run `azure resource create` + +echo "Check if the account already exists" +echo $resource_group_name | azure storage account show $storage_account_name +RET=$? + +if [ $RET != 0 ]; then + echo "Creating account" + azure resource create $resource_group_name $storage_account_name \ + Microsoft.Storage/storageAccounts "West Europe" \ + 2015-05-01-preview \ + -p "{\"accountType\":\"Standard_LRS\"}" + # Horrible workaround. It fails once with Error:null + if [ $? != 0 ] && grep 'Error: null' ~/.azure/azure.err; then + echo "Warning: Ignoring 'Error: null' error... The account gets created anyway" + else + exit 1 + fi +else + echo "Warning: $storage_account_name account already exists in $storage_service_name. Not creating it." +fi + +sleep 30 +echo "Retrieving the account key from the command line azure client" +echo $resource_group_name | \ + azure storage account keys list $storage_account_name --json > ${account_key_file}.json + +sed -n 's/.*"key1":.*"\(.*\)".*/\1/p' < ${account_key_file}.json > $account_key_file || exit 1 + +echo "Account $storage_account_name created, key in file $account_key_file." + diff --git a/azure/azure-create-vm.sh b/azure/azure-create-vm.sh new file mode 100755 index 0000000..b866f90 --- /dev/null +++ b/azure/azure-create-vm.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +if [ $# -lt 1 ]; then + cat < + +Creates a new VM in a specified resource group." +EOF + exit 1 +fi + +resource_group_name=$1; shift +vm_name=$1; shift +storage_account_name=$1; shift +ssh_public_key=$1; shift +subscription_id=$1; shift +network_name=$1; shift +ip=$1; shift + +echo "Check if the VM already exists" +azure vm list --resource-group $resource_group_name | cat | grep $vm_name -q +RET=$? + +if [ $RET != 0 ]; then + echo "Creating VM" + azure config mode arm + azure vm create --resource-group $resource_group_name \ + --name $vm_name \ + -l "West Europe" \ + --image-urn Canonical:UbuntuServer:14.04.3-LTS:14.04.201508050 \ + --vm-size Basic_A3 \ + --storage-account-name $storage_account_name \ + --os-type Linux \ + --ssh-publickey-file $ssh_public_key \ + -u ubuntu -p Password1* \ + --nic-id /subscriptions/$subscription_id/resourceGroups/$resource_group_name/providers/Microsoft.Network/networkInterfaces/$network_name +else + echo "Warning: $vm_name VM already exists in $resource_group_name. Not creating it." +fi diff --git a/azure/azure-delete-environment.sh b/azure/azure-delete-environment.sh new file mode 100755 index 0000000..53b9644 --- /dev/null +++ b/azure/azure-delete-environment.sh @@ -0,0 +1,82 @@ +#!/bin/sh + +if [ $# -lt 1 ]; then + cat < + +Deletes all the objects in the given environment. +EOF + exit 1 +fi + +deploy_env=$1; shift +resource_group=${deploy_env}-cf-hosted-service + +set -e + +echo "Resources for this group:" +azure resource list | grep -- ${resource_group} | awk '{ print $3, $4, $5 }' + +echo "====================================================================" +echo "Deleting VMs" + +for vm in $(azure resource list | grep -- ${resource_group} | grep virtualMachines | awk '{print $3}'); do + echo "Deleting VM $vm" + azure vm delete $resource_group $vm -q +done + +echo "====================================================================" +echo "Deleting Load Balancers" + +for lb in $(azure resource list | grep -- ${resource_group} | grep loadBalancers | awk '{print $3}'); do + echo "Deleting Load Balancer $lb" + azure network lb delete $resource_group $lb -q +done + +echo "====================================================================" +echo "Deleting Public IPs" + +for ip in $(azure resource list | grep -- ${resource_group} | grep publicIPAddresses | awk '{print $3}'); do + echo "Deleting IP $ip" + azure network public-ip delete $resource_group $ip -q +done + +echo "====================================================================" +echo "Deleting NICs" + +for nic in $(azure resource list | grep -- ${resource_group} | grep networkInterfaces | awk '{print $3}'); do + echo "Deleting NIC $nic" + azure network nic delete $resource_group $nic -q +done + +echo "====================================================================" +echo "Deleting Networks" + +for vnet in $(azure resource list | grep -- ${resource_group} | grep virtualNetworks | awk '{print $3}'); do + echo "Deleting Network $vnet" + azure network vnet delete $resource_group $vnet -q +done + +echo "====================================================================" +echo "Deleting Storage" + +for storage_account in $(azure resource list | grep -- ${resource_group} | grep storageAccounts | awk '{print $3}'); do + echo "Deleting Storage $storage_account" + export AZURE_STORAGE_CONNECTION_STRING=$(echo $resource_group | azure storage account connectionstring show $storage_account | awk '/connectionstring:/ {print $3}') + for container in $(azure storage container list | grep data | sed 1,2d | awk '{print $2}'); do + echo "Deleting container $container" + azure storage container delete $container -q + done + echo "Deleting Storage account $storage_account" + azure storage account delete -g $resource_group $storage_account -q +done + +echo "====================================================================" +echo "Deleting Resource Group" +if ! azure group list | grep -q $resource_group; then + echo "Group not found" +else + azure group delete $resource_group -q +fi + diff --git a/azure/azure-generate-account-settings.sh b/azure/azure-generate-account-settings.sh new file mode 100755 index 0000000..d5fc729 --- /dev/null +++ b/azure/azure-generate-account-settings.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +set -e + +if [ $# -lt 1 ]; then + cat < + +Initialises your account with the required account objects for BOSH on azure, +and creates a generated.azure_settings.sh file with all the environment +variables needed for terraform. + +EOF + exit 1 +fi +deploy_env=$1; shift + +if [ -s generated.azure_account_settings.sh ]; then + echo "File 'generated.azure_account_settings.sh' already exists. Skipping initialisation." + exit 0 +fi + +azure_subscription_id=$(azure account list | cat | sed -n 4p | awk '{ print $3 }') +azure_tenant_id=$(azure account list | cat | sed -n 4p | awk '{ print $4 }') + +azure_client_secret=$(openssl rand -base64 16) + +SERVICE_PASSWORD=$azure_client_secret ./azure-create-service-principal.sh $deploy_env $azure_subscription_id + +( +echo "# Load the file 'source ./generated.azure_account_settings.sh' to load these variables: " +echo "export TF_VAR_azure_subscription_id=$azure_subscription_id" +echo "export TF_VAR_azure_tenant_id=$azure_tenant_id" +echo "export TF_VAR_azure_client_secret='$azure_client_secret'" +echo "export TF_VAR_azure_client_id=$(< generated.application_id)" +) | tee generated.azure_account_settings.sh + + diff --git a/azure/azure-upload-certificate.sh b/azure/azure-upload-certificate.sh new file mode 100755 index 0000000..fddb16f --- /dev/null +++ b/azure/azure-upload-certificate.sh @@ -0,0 +1,29 @@ +#!/bin/sh +set -e + +azure config mode asm + +if azure service cert list | cat | grep -q $1-cf-bastion-service; then + echo "There is already a key for this service: $1-cf-bastion-service" +else + openssl req -x509 \ + -key ssh/insecure-deployer \ + -nodes \ + -days 365 -newkey rsa:2048 \ + -out generated.insecure-deployer.pem \ + -subj '/CN=www.mydom.com/O=My Company Name LTD./C=US' + + openssl x509 \ + -outform der \ + -in generated.insecure-deployer.pem \ + -out generated.insecure-deployer.pfx + + azure service cert create $1-cf-bastion-service generated.insecure-deployer.pfx +fi + +sleep 15 # Wait for the object to be created + +azure service cert list | \ + grep $1-cf-bastion-service | \ + awk '{print $3}' | head -n 1 | tr -d '\n' > generated.ssh_thumbprint + diff --git a/azure/bastion.tf b/azure/bastion.tf new file mode 100644 index 0000000..b1d1b04 --- /dev/null +++ b/azure/bastion.tf @@ -0,0 +1,96 @@ +resource "template_file" "manifest" { + filename = "${path.module}/manifest.yml.tpl" + + depends_on = "azure_hosted_service.bastion" + + vars { + # From `azure account list` + azure_subscription_id = "${var.azure_subscription_id}" + azure_tenant_id = "${var.azure_tenant_id}" + + # Created by `azure-create-service-principal.sh` + azure_client_id = "${var.azure_client_id}" + # Password passed to the script above + azure_client_secret = "${var.azure_client_secret}" + + # Created in terraform when setting up azure_hosted_service + azure_resource_group_name = "${var.env}-cf-hosted-service" + + # created with azure network command (terraform network does not support assign group name) + azure_vnet_name = "${var.env}-cf-network" + azure_subnet_name = "${var.env}-cf-subnet" + + # Created with azure-create-storage-service.sh called from terraform. + azure_storage_account_name = "${var.env}cfstgaccount" + + # Created with azure-create-storage-service.sh called from terraform + # Stored in generated.cf-storage-account.key + azure_storage_access_key = "${join("\\\\n", split("\n", file("generated.cf-storage-account.key")))}" + + # Output of this command. x509 request of the SSH key. + # Needs to be created in one line + azure_ssh_certificate = "${join("\\\\n", split("\n", file("generated.insecure-deployer.pem")))}" + + bosh_public_ip = "${replace(file("generated.bosh-public-ip"), "\n", "")}" + } +} + +resource "azure_hosted_service" "bastion" { + name = "${var.env}-cf-bastion-service" + location = "West Europe" + ephemeral_contents = false + description = "Hosted service for the CF bastion host." + label = "${var.env}-cf-bastion-hs-01" + provisioner "local-exec" { + command = "./azure-upload-certificate.sh ${var.env}" + } +} + +resource "azure_instance" "bastion" { + name = "${var.env}-cf-bastion" + hosted_service_name = "${azure_hosted_service.bastion.name}" + depends_on = "azure_hosted_service.bastion" + depends_on = "azure_virtual_network.bastion" + depends_on = "azure_storage_service.cf-storage" + image = "Ubuntu Server 14.04 LTS" + size = "${var.bastion_instance_size}" + storage_service_name = "${var.env}cfstorage" + location = "West Europe" + virtual_network = "${var.env}-bastion-network" + subnet = "${var.env}-bastion-subnet" + + username = "${var.ssh_user}" + ssh_key_thumbprint = "${file("generated.ssh_thumbprint")}" + + endpoint { + name = "SSH" + protocol = "tcp" + public_port = 22 + private_port = 22 + } + + provisioner "local-exec" { + command = "./azure-acl-rule.sh ${var.env}-cf-bastion SSH 10 permit ${var.office_cidrs}" + } + + provisioner "remote-exec" { + inline = ["cat << EOF > /home/ubuntu/manifest.yml", + "${template_file.manifest.rendered}", + "EOF"] + } + + provisioner "file" { + source = "${path.module}/ssh/insecure-deployer" + destination = "/home/ubuntu/.ssh/id_rsa" + } + + provisioner "file" { + source = "${path.module}/ssh/insecure-deployer.pub" + destination = "/home/ubuntu/.ssh/id_rsa.pub" + } + + provisioner "file" { + source = "${path.module}/provision.sh" + destination = "/home/ubuntu/provision.sh" + } +} diff --git a/azure/generated.ssh_thumbprint b/azure/generated.ssh_thumbprint new file mode 100644 index 0000000..7a70aca --- /dev/null +++ b/azure/generated.ssh_thumbprint @@ -0,0 +1 @@ +AD7491BFB06C711227FE653B0B290D1E652610BC \ No newline at end of file diff --git a/azure/globals.tf b/azure/globals.tf new file mode 120000 index 0000000..6a51cd9 --- /dev/null +++ b/azure/globals.tf @@ -0,0 +1 @@ +../globals.tf \ No newline at end of file diff --git a/azure/hosted_service.tf b/azure/hosted_service.tf new file mode 100644 index 0000000..a893d63 --- /dev/null +++ b/azure/hosted_service.tf @@ -0,0 +1,7 @@ +resource "azure_hosted_service" "cf-hosted-service" { + name = "${var.env}-cf-hosted-service" + location = "West Europe" + ephemeral_contents = false + description = "Hosted service for all the CF objects" + label = "${var.env}-cf-hs-01" +} diff --git a/azure/manifest.yml.tpl b/azure/manifest.yml.tpl new file mode 100755 index 0000000..ae935b1 --- /dev/null +++ b/azure/manifest.yml.tpl @@ -0,0 +1,142 @@ +# +# Template based on the file http://cloudfoundry.blob.core.windows.net/misc/bosh.yml +# as described in https://github.com/Azure/bosh-azure-cpi-release/blob/master/docs/guide.md#1-create-a-deployment-manifest +# +--- +name: bosh + +releases: +- name: bosh + url: http://cloudfoundry.blob.core.windows.net/bosh/bosh-168+dev.preview2.tgz + sha1: 363a42a0101b9cf822178a959ba36bd4de71c5f3 +- name: bosh-azure-cpi + url: http://cloudfoundry.blob.core.windows.net/azurecpi/bosh-azure-cpi-0+dev.preview2.tgz + sha1: 5b46a278ce20e3712e7aad5396c5b0d2b93695cd + +networks: +- name: private + type: manual + subnets: + - range: 10.0.0.0/24 + gateway: 10.0.0.1 + dns: [8.8.8.8] + cloud_properties: + virtual_network_name: ${azure_vnet_name} # <--- Replace with virtual network name + subnet_name: ${azure_subnet_name} # <--- Replace with subnet name for BOSH VM +- name: public + type: vip + cloud_properties: + tcp_endpoints: + - "22:22" + - "6868:6868" + +resource_pools: +- name: vms + network: private + stemcell: + url: http://cloudfoundry.blob.core.windows.net/stemcell/stemcell.preview2.tgz + sha1: b05121f774aeaedbd66f6e735339be5c1bf85a5b + cloud_properties: + instance_type: Standard_D1 + +disk_pools: +- name: disks + disk_size: 25_000 + +jobs: +- name: bosh + templates: + - {name: nats, release: bosh} + - {name: redis, release: bosh} + - {name: postgres, release: bosh} + - {name: blobstore, release: bosh} + - {name: director, release: bosh} + - {name: health_monitor, release: bosh} + - {name: registry, release: bosh} + - {name: cpi, release: bosh-azure-cpi} + + instances: 1 + resource_pool: vms + persistent_disk_pool: disks + + networks: + - {name: private, static_ips: [10.0.0.5], default: [dns, gateway]} + - {name: public, static_ips: [${bosh_public_ip}]} + + properties: + nats: + address: 127.0.0.1 + user: nats + password: nats-password + + redis: + listen_addresss: 127.0.0.1 + address: 127.0.0.1 + password: redis-password + + postgres: &db + host: 127.0.0.1 + user: postgres + password: postgres-password + database: bosh + adapter: postgres + + registry: + address: 10.0.0.5 + host: 10.0.0.5 + db: *db + http: {user: admin, password: admin, port: 25777} + username: admin + password: admin + port: 25777 + + blobstore: + address: 10.0.0.5 + port: 25250 + provider: dav + director: {user: director, password: director-password} + agent: {user: agent, password: agent-password} + + director: + address: 127.0.0.1 + name: bosh + db: *db + cpi_job: cpi + enable_snapshots: true + + hm: + http: {user: hm, password: hm-password} + director_account: {user: admin, password: admin} + + azure: &azure + environment: AzureCloud + subscription_id: "${azure_subscription_id}" # <--- Replace with your subscription id + storage_account_name: "${azure_storage_account_name}" # <--- Replace with your storage account name + storage_access_key: "${azure_storage_access_key}" # <--- Replace with the access key of your storage account + resource_group_name: "${azure_resource_group_name}" # <--- Replace with your resource group name + tenant_id: "${azure_tenant_id}" # <--- Replace with your tenant id of the service principal + client_id: "${azure_client_id}" # <--- Replace with your client id of the service principal + client_secret: "${azure_client_secret}" # <--- Replace with your client secret of the service principal + ssh_user: vcap + ssh_certificate: "${azure_ssh_certificate}" # <--- Replace with the content of your ssh certificate + + agent: {mbus: "nats://nats:nats-password@10.0.0.5:4222"} + + ntp: &ntp [0.north-america.pool.ntp.org] + +cloud_provider: + template: {name: cpi, release: bosh-azure-cpi} + + ssh_tunnel: + host: ${bosh_public_ip} + port: 22 + user: vcap # The user must be as same as above ssh_user + private_key: ~/.ssh/id_rsa # Path relative to this manifest file + + mbus: https://mbus-user:mbus-password@${bosh_public_ip}:6868 + + properties: + azure: *azure + agent: {mbus: "https://mbus-user:mbus-password@0.0.0.0:6868"} + blobstore: {provider: local, path: /var/vcap/micro_bosh/data/cache} + ntp: *ntp diff --git a/azure/network.tf b/azure/network.tf new file mode 100644 index 0000000..90c33b5 --- /dev/null +++ b/azure/network.tf @@ -0,0 +1,37 @@ +resource "azure_virtual_network" "bastion" { + name = "${var.env}-bastion-network" + address_space = ["${var.virtual_network_cidr}"] + location = "West Europe" + + subnet { + name = "${var.env}-bastion-subnet" + address_prefix = "${var.bastion_cidr}" + } +} + +# Fake resource to call a external command. +# Creates the network for cloudfoundry +resource "template_file" "cf-network" { + filename = "/dev/null" + depends_on = "azure_hosted_service.cf-hosted-service" + provisioner { + local-exec { + # Sadly, sleep 30 to wait for the hosted service to be created in Azure + command = "sleep 30 && ./azure-create-network.sh ${var.env}-cf-hosted-service ${var.env}-cf-network ${var.virtual_network_cidr} ${var.env}-cf-subnet ${var.cf_cidr}" + } + } +} + +# Fake resource to call a external command. +# Creates a public ip for microbosh +resource "template_file" "bosh-public-ip" { + filename = "/dev/null" + depends_on = "azure_hosted_service.cf-hosted-service" + provisioner { + local-exec { + # Sadly, sleep 30 to wait for the hosted service to be created in Azure + command = "sleep 30 && ./azure-create-public-ip.sh ${var.env}-cf-hosted-service bosh-public-ip" + } + } +} + diff --git a/azure/output.tf b/azure/output.tf new file mode 100644 index 0000000..5b11f57 --- /dev/null +++ b/azure/output.tf @@ -0,0 +1,14 @@ +output "bastion-vips" { + value = "${join(",", azure_instance.bastion.*.vip_address)}" +} +output "bastion-ips" { + value = "${join(",", azure_instance.bastion.*.ip_address)}" +} +output "bastion_ip" { + value = "${azure_instance.bastion.0.vip_address}" +} + +#output "manifest" { +# value = "${template_file.manifest.rendered}" +#} + diff --git a/azure/provider.tf b/azure/provider.tf new file mode 100644 index 0000000..f808791 --- /dev/null +++ b/azure/provider.tf @@ -0,0 +1,3 @@ +provider "azure" { + settings_file="${file("${var.azure_credentials_file}")}" +} diff --git a/azure/provision.sh b/azure/provision.sh new file mode 100644 index 0000000..f89ba9e --- /dev/null +++ b/azure/provision.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Prepare the jumpbox to be able to install ruby and git-based bosh and cf repos +cd $HOME + +PACKAGES="build-essential git zlibc zlib1g-dev ruby ruby-dev openssl libxslt-dev libxml2-dev libssl-dev libreadline6 libreadline6-dev libyaml-dev libsqlite3-dev sqlite3" +if ! dpkg -l $PACKAGES > /dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y $PACKAGES +fi + +# Set correct permissions for the ssh key we copied +chmod 400 ~/.ssh/id_rsa +chmod 400 ~/.ssh/id_rsa.pub + +# start the ssh-agent and add the keys +eval `ssh-agent` +#ssh-add ~/.ssh/insecure-deployer +ssh-add ~/.ssh/id_rsa + +if [ ! -x bosh-init ]; then + wget https://s3.amazonaws.com/bosh-init-artifacts/bosh-init-0.0.72-linux-amd64 -O bosh-init + chmod +x bosh-init +fi + +./bosh-init deploy manifest.yml diff --git a/azure/storage.tf b/azure/storage.tf new file mode 100644 index 0000000..584f5cd --- /dev/null +++ b/azure/storage.tf @@ -0,0 +1,23 @@ +resource "azure_storage_service" "cf-storage" { + name = "${var.env}cfstorage" + location = "West Europe" + description = "Default storage for CF installation" + account_type = "Standard_LRS" +} + +# Fake resource to call a external command. +# +# Creates a storage account for cloudfoundry, by calling ./azure-create-storage-service.sh +# +resource "template_file" "cf-storage-account" { + filename = "/dev/null" + depends_on = "azure_hosted_service.cf-hosted-service" + provisioner { + local-exec { + # Sadly, sleep 30 to wait for the hosted service to be created in Azure + command = "sleep 30 && ./azure-create-storage-service.sh ${var.env}-cf-hosted-service ${var.env}cfstgaccount generated.cf-storage-account.key" + } + } +} + + diff --git a/azure/variables.tf b/azure/variables.tf new file mode 100644 index 0000000..f1f53ea --- /dev/null +++ b/azure/variables.tf @@ -0,0 +1,37 @@ +variable "azure_credentials_file" { + description = "JSON Account Credentials file for Azure" + default = "credentials.publishsettings" +} + +variable "virtual_network_cidr" { + description = "CIDR for the virtual network" + default = "10.0.0.0/16" +} + +variable "bastion_cidr" { + description = "CIDR for bastion network" + default = "10.0.0.0/24" +} + +variable "cf_cidr" { + description = "CIDR for cloudfoundry network" + default = "10.0.0.0/24" +} + +variable "bastion_instance_size" { + description = "Size for the bastion host" + default = "Basic_A3" +} + +variable "azure_subscription_id" { + description = "Azure subscription id (comes from 'azure account list')" +} +variable "azure_tenant_id" { + description = "Azure tenant id (comes from 'azure account list')" +} +variable "azure_client_id" { + description = "Azure client id (comes from the bosh app creation)" +} +variable "azure_client_secret" { + description = "Azure client id (generated by us)" +}