From d4401d389c26e661790508d9012229c993b36401 Mon Sep 17 00:00:00 2001 From: Alvaro A Date: Thu, 25 Jun 2020 17:17:40 +0200 Subject: [PATCH] First draft version --- .gitignore | 3 + .gitlab-ci.yml | 66 +++++++++++++++++++++ Dockerfile-terraform | 17 ++++++ ansible_inventory.yml | 52 +++++++++++++++++ create_vars_json.py | 48 +++++++++++++++ inventory.tfvars.json.example | 93 ++++++++++++++++++++++++++++++ inventory_vars.tfvars.json.example | 12 ++++ terraform.tfvars.json | 4 ++ vsphere_data.tf | 68 ++++++++++++++++++++++ vsphere_vms.tf | 63 ++++++++++++++++++++ 10 files changed, 426 insertions(+) create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 Dockerfile-terraform create mode 100644 ansible_inventory.yml create mode 100755 create_vars_json.py create mode 100644 inventory.tfvars.json.example create mode 100644 inventory_vars.tfvars.json.example create mode 100644 terraform.tfvars.json create mode 100644 vsphere_data.tf create mode 100644 vsphere_vms.tf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3918d15 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.terraform/ +terraform +inventory*.tfvars.json diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..2ad09ab --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,66 @@ +stages: + - inventory + - deploy + #- configure + +.aggregate_rules: &aggregate_rules + - if: '$CI_COMMIT_BRANCH == "master"' + when: on_success + - when: never + +variables: &cd_vars + REGISTRY: "localhost:5000" + ANSIBLE_REMOTE_USER: "admin" + ANSIBLE_STDOUT_CALLBACK: "yaml" + ANSIBLE_HOST_KEY_CHECKING: "False" + ANSIBLE_SSH_PIPELINING: "True" + VARS_JSON: "data/inventory_vars.tfvars.json" + INVENTORY_JSON: "data/inventory.tfvars.json" + TF_INPUT: 0 + TF_LOG: "TRACE" + TF_IN_AUTOMATION: "1" + TF_DATA_DIR: "/opt/.terraform" + INVENTORY: "ansible_inventory.yml" + # MOVE THESE TO VAULT OR AS MASKED VARIABLES + VSPHERE_USER: "administrator@vsphere.local" + VSPHERE_PASSWORD: "adminpass123" + +prepare: + image: ${REGISTRY}/ansible/ansible2.7 + stage: inventory + rules: *aggregate_rules + allow_failure: false + artifacts: + paths: + - "data/*.tfvars.json" + before_script: + - mkdir data/ + script: + - ansible-inventory -i $INVENTORY --list > $INVENTORY_JSON + - python create_vars_json.py $INVENTORY_JSON + +#TODO: set artifactory backend +terraform: + image: ${REGISTRY}/terraform/terraform:0.12.26 + stage: deploy + rules: *aggregate_rules + dependencies: + - prepare + allow_failure: false + script: + - terraform init -plugin-dir="$TF_DATA_DIR/plugins" + - | + if [[ -n "$TAINT_GUEST" ]]; then + terraform taint 'vsphere_virtual_machine.vsphere_vms\"${TAINT_GUEST}\"]' -state="${INVENTORY}.tfstate" + fi + - terraform plan -var="vsphere_user=${VSPHERE_USER}" -var="vsphere_password=${VSPHERE_PASSWORD}" -var-file="$VARS_JSON" -var-file="$INVENTORY_JSON" -state="${INVENTORY}.tfstate" -out=tfplan + - terraform apply -state="${INVENTORY}.tfstate" tfplan + +## A following stage would be used to configure the VMs with ansible, using the original ansible inventory file +#ansible: +# image: ${REGISTRY}/ansible/ansible2.7 +# stage: configure +# rules: *aggregate_rules +# allow_failure: false +# script: +# - ansible-playbook -i ansible_inventory.yml configure.yml diff --git a/Dockerfile-terraform b/Dockerfile-terraform new file mode 100644 index 0000000..7ae639f --- /dev/null +++ b/Dockerfile-terraform @@ -0,0 +1,17 @@ +FROM centos:centos7 + +ARG TERRAFORM_VERSION=0.12.26 +ARG VSPHERE_PLUGIN_VERSION=1.18.3 +ENV TF_DATA_DIR="/opt/.terraform" + +RUN yum install -y wget unzip && \ + wget https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip && \ + unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip && \ + mv terraform /usr/bin && \ + rm -rf terraform_${TERRAFORM_VERSION}_linux_amd64.zip + +RUN wget https://releases.hashicorp.com/terraform-provider-vsphere/1.18.3/terraform-provider-vsphere_${VSPHERE_PLUGIN_VERSION}_linux_amd64.zip && \ + unzip terraform-provider-vsphere_${VSPHERE_PLUGIN_VERSION}_linux_amd64.zip && \ + mkdir -p ${TF_DATA_DIR}/plugins && \ + mv terraform-provider-vsphere_v${VSPHERE_PLUGIN_VERSION}_x4 ${TF_DATA_DIR}/plugins && \ + rm -rf terraform-provider-vsphere_${VSPHERE_PLUGIN_VERSION}_linux_amd64.zip diff --git a/ansible_inventory.yml b/ansible_inventory.yml new file mode 100644 index 0000000..aa24536 --- /dev/null +++ b/ansible_inventory.yml @@ -0,0 +1,52 @@ +all: + children: + tf_group: + hosts: + tf-test: + name: "tf-test" + network_adapters: + - name: "VM Network" + ip: "172.16.0.170" + netmask: '255.255.255.0' + gateway: "172.16.0.1" + ipv6: "fcf8:ab17:01fd::170" + netmaskv6: "64" + gatewayv6: "fcf8:ab17:01fd::1" + - name: "VM Network2" + ip: "172.17.0.170" + netmask: '255.255.255.0' + ipv6: "fcf8:ab17:02fd::170" + netmaskv6: "64" + disk_layout: + - size_gb: "150" + type: "thin" + datastore: "datastore-01" + tf-test2: + name: "tf-test2" + network_adapters: + - name: "VM Network" + ip: "172.16.0.171" + netmask: '255.255.255.0' + gateway: "172.16.0.1" + ipv6: "fcf8:ab17:01fd::171" + netmaskv6: "64" + gatewayv6: "fcf8:ab17:01fd::1" + start_connected: True + - name: "VM Network2" + ip: "172.17.0.171" + netmask: '255.255.255.0' + ipv6: "fcf8:ab17:02fd::171" + netmaskv6: "64" + start_connected: True + disk_layout: + - size_gb: "150" + type: "thin" + datastore: "datastore-01" + vars: + guest_id: centos64Guest + guest_memory: 2048 + guest_vcpu: 2 + dns_servers: + - 8.8.8.8 + - 8.8.4.4 + guest_template: vm-template1 diff --git a/create_vars_json.py b/create_vars_json.py new file mode 100755 index 0000000..1889e13 --- /dev/null +++ b/create_vars_json.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +import os +import sys +import json + +def create_vars_json(inventories): + output_file = os.environ.get("VARS_JSON", "inventory_vars.tfvars.json") + + variables = dict(networks=list(),templates=list(),datastores=list()) + + for inventory_path in inventories: + + with open(inventory_path,"r") as inventory_file: + inventory = json.loads(inventory_file.read()) + + hostvars = inventory["_meta"]["hostvars"] + variables["networks"].extend(net_if["name"] + for var in hostvars.values() + for net_if in var["network_adapters"]) + + variables["templates"].extend(var["guest_template"] + for var in hostvars.values() + if "guest_template" in var) + + variables["datastores"].extend(disk_layout["datastore"] + for var in hostvars.values() + for disk_layout in var["disk_layout"] if "datastore" in disk_layout) + + if os.path.isfile(output_file): + existing_vars = json.loads(open(output_file, "r").read()) + for k in variables: + if k in existing_vars: + variables[k].extend(existing_vars[k]) + + for k in variables: + variables[k] = list(set(variables[k])) + + with open(output_file, "w") as nt: + json.dump(variables, nt, indent=2) + + +if __name__ == '__main__': + if len(sys.argv) < 2: + print("Specify inventory paths as arguments!") + sys.exit(1) + print(str(sys.argv[1:])) + create_vars_json(sys.argv[1:]) diff --git a/inventory.tfvars.json.example b/inventory.tfvars.json.example new file mode 100644 index 0000000..fe54c16 --- /dev/null +++ b/inventory.tfvars.json.example @@ -0,0 +1,93 @@ +{ + "_meta": { + "hostvars": { + "tf-test": { + "disk_layout": [ + { + "datastore": "datastore-01", + "size_gb": "150", + "type": "thin" + } + ], + "dns_servers": [ + "8.8.8.8", + "8.8.4.4" + ], + "guest_id": "centos64Guest", + "guest_memory": 2048, + "guest_template": "vm-template1", + "guest_vcpu": 2, + "name": "tf-test", + "network_adapters": [ + { + "gateway": "172.16.0.1", + "gatewayv6": "fcf8:ab17:01fd::1", + "ip": "172.16.0.170", + "ipv6": "fcf8:ab17:01fd::170", + "name": "VM Network", + "netmask": "255.255.255.0", + "netmaskv6": "64" + }, + { + "ip": "172.17.0.170", + "ipv6": "fcf8:ab17:02fd::170", + "name": "VM Network2", + "netmask": "255.255.255.0", + "netmaskv6": "64" + } + ] + }, + "tf-test2": { + "disk_layout": [ + { + "datastore": "datastore-01", + "size_gb": "150", + "type": "thin" + } + ], + "dns_servers": [ + "8.8.8.8", + "8.8.4.4" + ], + "guest_id": "centos64Guest", + "guest_memory": 2048, + "guest_template": "vm-template1", + "guest_vcpu": 2, + "name": "tf-test2", + "network_adapters": [ + { + "gateway": "172.16.0.1", + "gatewayv6": "fcf8:ab17:01fd::1", + "ip": "172.16.0.171", + "ipv6": "fcf8:ab17:01fd::171", + "name": "VM Network", + "netmask": "255.255.255.0", + "netmaskv6": "64", + "start_connected": true + }, + { + "ip": "172.17.0.171", + "ipv6": "fcf8:ab17:02fd::171", + "name": "VM Network2", + "netmask": "255.255.255.0", + "netmaskv6": "64", + "start_connected": true + } + ] + } + } + }, + "all": { + "children": [ + "tf_group", + "ungrouped" + ] + }, + "tf_group": { + "hosts": [ + "tf-test", + "tf-test2" + ] + }, + "ungrouped": {} +} diff --git a/inventory_vars.tfvars.json.example b/inventory_vars.tfvars.json.example new file mode 100644 index 0000000..d6f6a92 --- /dev/null +++ b/inventory_vars.tfvars.json.example @@ -0,0 +1,12 @@ +{ + "datastores": [ + "datastore-01" + ], + "templates": [ + "vm-template1" + ], + "networks": [ + "VM Network2", + "VM Network" + ] +} diff --git a/terraform.tfvars.json b/terraform.tfvars.json new file mode 100644 index 0000000..8515cf6 --- /dev/null +++ b/terraform.tfvars.json @@ -0,0 +1,4 @@ +{ + "vsphere_server": "192.168.0.10", + "allow_unverified_ssl": false +} diff --git a/vsphere_data.tf b/vsphere_data.tf new file mode 100644 index 0000000..d85d4cc --- /dev/null +++ b/vsphere_data.tf @@ -0,0 +1,68 @@ +variable "vsphere_user" { + type = string +} +variable "vsphere_password" { + type = string +} +variable "vsphere_server" { + type = string +} +variable "allow_unverified_ssl" { + type = bool +} + +provider "vsphere" { + user = var.vsphere_user + password = var.vsphere_password + vsphere_server = var.vsphere_server + allow_unverified_ssl = var.allow_unverified_ssl + version = "~> 1.18" +} + +variable "networks" { + type = list(string) +} + +variable "templates" { + type = list(string) +} + +variable "datastores" { + type = list(string) +} + +data "vsphere_datacenter" "dc" { + name = "ha-datacenter" +} + +data "vsphere_compute_cluster" "ha-cluster" { + name = "ha-cluster" + datacenter_id = data.vsphere_datacenter.dc.id +} + +data "vsphere_datastore" "all" { + count = length(var.datastores) + + name = var.datastores[count.index] + datacenter_id = data.vsphere_datacenter.dc.id +} + +data "vsphere_distributed_virtual_switch" "dvs1" { + name = "dvs1" + datacenter_id = data.vsphere_datacenter.dc.id +} + +data "vsphere_network" "all" { + count = length(var.networks) + + name = var.networks[count.index] + datacenter_id = data.vsphere_datacenter.dc.id + distributed_virtual_switch_uuid = data.vsphere_distributed_virtual_switch.dvs1.id +} + +data "vsphere_virtual_machine" "templates" { + count = length(var.templates) + + name = var.templates[count.index] + datacenter_id = data.vsphere_datacenter.dc.id +} diff --git a/vsphere_vms.tf b/vsphere_vms.tf new file mode 100644 index 0000000..eb3a725 --- /dev/null +++ b/vsphere_vms.tf @@ -0,0 +1,63 @@ +variable "_meta" { + type = map +} + +resource "vsphere_virtual_machine" "vsphere_vms" { + for_each = var._meta.hostvars + + name = each.key + resource_pool_id = data.vsphere_compute_cluster.ha-cluster.resource_pool_id + #datastore is defined for each disk + #datastore_id = data.vsphere_datastore.all.0.id + + num_cpus = each.value.guest_vcpu + memory = each.value.guest_memory + guest_id = [ for i in data.vsphere_virtual_machine.templates : i.guest_id if i.name == each.value.guest_template ][0] + scsi_type = [ for i in data.vsphere_virtual_machine.templates : i.scsi_type if i.name == each.value.guest_template ][0] + firmware = "bios" + + dynamic "network_interface" { + for_each = each.value.network_adapters + + content { + network_id = [ for i in data.vsphere_network.all : i.id if i.name == network_interface.value.name ][0] + adapter_type = "vmxnet3" + } + } + + dynamic "disk" { + for_each = each.value.disk_layout + + content { + label = "disk${disk.key}" + datastore_id = [ for i in data.vsphere_datastore.all : i.id if i.name == disk.value.datastore ][0] + size = disk.value.size_gb + thin_provisioned = disk.value.type == "thin" ? true : false + } + } + + clone { + template_uuid = data.vsphere_virtual_machine.templates.0.id + customize { + linux_options { + host_name = each.key + domain = "mydomain.com" + } + + dynamic "network_interface" { + for_each = each.value.network_adapters + + content { + ipv4_address = network_interface.value.ip + ipv4_netmask = "24" #tonumber(network_interface.value.netmask) + ipv6_address = network_interface.value.ipv6 + ipv6_netmask = tonumber(network_interface.value.netmaskv6) + } + } + + dns_server_list = each.value.dns_servers + ipv4_gateway = [ for i in each.value.network_adapters : i.gateway if contains(keys(i), "gateway") ][0] + ipv6_gateway = [ for i in each.value.network_adapters : i.gatewayv6 if contains(keys(i), "gatewayv6") ][0] + } + } +}