From afb0513224718a82ee6f2937dd39296a35857b0b Mon Sep 17 00:00:00 2001
From: Andrea Galbusera <gizero@gmail.com>
Date: Tue, 12 Jan 2021 16:53:44 +0100
Subject: [PATCH] initial infra on Hetzner Cloud

---
 .gitignore                   | 35 +++++++++++++++
 .gitlab-ci.yml               | 85 ++++++++++++++++++++++++++++++++++++
 .terraform.lock.hcl          | 21 +++++++++
 backend.tf                   |  4 ++
 infra.tf                     | 16 +++++++
 provider-hcloud.tf           |  5 +++
 provision.tfvars.sample      |  2 +
 templates/cloud-init.yml.tpl | 61 ++++++++++++++++++++++++++
 versions.tf                  |  9 ++++
 9 files changed, 238 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 .gitlab-ci.yml
 create mode 100755 .terraform.lock.hcl
 create mode 100644 backend.tf
 create mode 100644 infra.tf
 create mode 100644 provider-hcloud.tf
 create mode 100644 provision.tfvars.sample
 create mode 100644 templates/cloud-init.yml.tpl
 create mode 100644 versions.tf

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..28370b5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,35 @@
+# Local .terraform directories
+**/.terraform/*
+
+# .tfstate files
+*.tfstate
+*.tfstate.*
+
+# Crash log files
+crash.log
+
+# Exclude all .tfvars files, which are likely to contain sentitive data, such as
+# password, private keys, and other secrets. These should not be part of version 
+# control as they are data points which are potentially sensitive and subject 
+# to change depending on the environment.
+#
+*.tfvars
+
+# Ignore override files as they are usually used to override resources locally and so
+# are not checked in
+override.tf
+override.tf.json
+*_override.tf
+*_override.tf.json
+
+# Include override files you do wish to add to version control using negated pattern
+#
+# !example_override.tf
+
+# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
+# example: *tfplan*
+*tfplan*
+
+# Ignore CLI configuration files
+.terraformrc
+terraform.rc
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..907e070
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,85 @@
+image:
+  name: hashicorp/terraform:light
+  entrypoint:
+    - ''
+
+variables:
+  PLAN: plan.tfplan
+  JSON_PLAN_FILE: plan.json
+  GITLAB_TF_ADDRESS: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${CI_PROJECT_NAME}"
+  TF_COMMON_ARGS: "-var hcloud_token=${HCLOUD_RW_TOKEN:=$HCLOUD_RO_TOKEN} -var theo_token=${THEO_TOKEN}"
+
+cache:
+  key: "$CI_COMMIT_SHA"
+  paths:
+    - .terraform
+
+.install-jq: &install-jq
+  - apk add --update jq
+  - alias convert_report="jq -r '([.resource_changes[].change.actions?]|flatten)|{\"create\":(map(select(.==\"create\"))|length),\"update\":(map(select(.==\"update\"))|length),\"delete\":(map(select(.==\"delete\"))|length)}'"
+
+before_script:
+  - *install-jq
+  - terraform version
+  - terraform init
+      -input=false
+      -reconfigure
+      -backend-config="address=${GITLAB_TF_ADDRESS}"
+      -backend-config="lock_address=${GITLAB_TF_ADDRESS}/lock"
+      -backend-config="unlock_address=${GITLAB_TF_ADDRESS}/lock"
+      -backend-config="username=gitlab-ci-token"
+      -backend-config="password=${CI_JOB_TOKEN}"
+      -backend-config="lock_method=POST"
+      -backend-config="unlock_method=DELETE"
+      -backend-config="retry_wait_min=5"
+
+stages:
+  - validate
+  - plan
+  - apply
+  - provision
+  - destroy
+
+validate:
+  stage: validate
+  script:
+    - terraform validate
+    - terraform fmt -check=true
+  only:
+    - merge_requests
+    - pushes
+
+merge review:
+  stage: plan
+  script:
+    - terraform plan ${TF_COMMON_ARGS} -out $PLAN
+    - terraform show --json $PLAN | convert_report > $JSON_PLAN_FILE
+  artifacts:
+    expire_in: 1 week
+    name: plan
+    reports:
+      terraform: $JSON_PLAN_FILE
+  only:
+    - merge_requests
+
+apply:
+  stage: apply
+  script:
+    - terraform apply ${TF_COMMON_ARGS} -auto-approve
+  only:
+    - master
+
+provision:
+  stage: provision
+  trigger:
+    project: fcub/fcub-infra-ansible
+  only:
+    - master
+
+destroy:
+  stage: destroy
+  script:
+    - terraform destroy ${TF_COMMON_ARGS} -auto-approve
+  when: manual
+  only:
+    - master
diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl
new file mode 100755
index 0000000..633416c
--- /dev/null
+++ b/.terraform.lock.hcl
@@ -0,0 +1,21 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hetznercloud/hcloud" {
+  version     = "1.23.0"
+  constraints = "1.23.0"
+  hashes = [
+    "h1:saM0Nkf+071U3kTYiPyOWFTxdLCPKEKAa5IK53Cu6JQ=",
+    "zh:158d1369f267ce292f6e278202c60a1b35f362d24b94d57c83ef460624ef4dc4",
+    "zh:237cf7934682a70b2a911ba7ad5ecef5e6f6c4c7e8c75d5419adee032b843ff3",
+    "zh:249a5c0c73852783ce9191076c1cb0cf9a1f53c7a1c3af08be83d7c51ab4d46c",
+    "zh:2d7e0cab7d54f4373c01210b178bdeca46279cfb0c794f8e564baa8cb95179ad",
+    "zh:5be24f23c00294363aeb07315a98f6c21e2718e6d28d24800d94a67f84ac1aee",
+    "zh:753e498bebb7e9f6423f3654528a429790f5e009a9d80b58733686de1970a05e",
+    "zh:848d8e300e92c5e35cf2dbf4d68a3624898f317e9f11cbbb35f4e5d8ff07b968",
+    "zh:85579efc6dd158dbc2eed22a628f4539e924cfbef2bf07f4173fa47ca6ccd37b",
+    "zh:879e7aa1d7825dd546b8edfc29037c60818e0ab87021430e0c06e051a96ab30a",
+    "zh:9354ff17884ff0fbdd5d81d5e430a6b8712ad763069a7345bc56bfc19b190813",
+    "zh:b2c52428c03ee7a438300692c18f2e1fe09afd4c107b69530a746574690b1bde",
+  ]
+}
diff --git a/backend.tf b/backend.tf
new file mode 100644
index 0000000..4ca44e9
--- /dev/null
+++ b/backend.tf
@@ -0,0 +1,4 @@
+terraform {
+  backend "http" {
+  }
+}
diff --git a/infra.tf b/infra.tf
new file mode 100644
index 0000000..980b55b
--- /dev/null
+++ b/infra.tf
@@ -0,0 +1,16 @@
+# dummy key will be used to avoid servers root passwords being mailed
+# will use AK to setup real authorization
+resource "hcloud_ssh_key" "dummy-key" {
+  name       = "dummy-key"
+  public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDG6KCXcL6bgJKD/ZbzLDeZms6TJwUz7K0yf+KCvfjxV dummy-key"
+}
+
+variable "theo_token" {}
+
+resource "hcloud_server" "fcub01" {
+  name        = "fcub01"
+  image       = "ubuntu-20.04"
+  server_type = "cpx31"
+  ssh_keys    = [hcloud_ssh_key.dummy-key.id]
+  user_data   = templatefile("templates/cloud-init.yml.tpl", { theo_token = var.theo_token })
+}
diff --git a/provider-hcloud.tf b/provider-hcloud.tf
new file mode 100644
index 0000000..51c035d
--- /dev/null
+++ b/provider-hcloud.tf
@@ -0,0 +1,5 @@
+variable "hcloud_token" {}
+
+provider "hcloud" {
+  token = var.hcloud_token
+}
diff --git a/provision.tfvars.sample b/provision.tfvars.sample
new file mode 100644
index 0000000..8b544c5
--- /dev/null
+++ b/provision.tfvars.sample
@@ -0,0 +1,2 @@
+hcloud_token = "YOUR_HETZNER_TOKEN"
+theo_token = "THEO_TOKEN_TO_REPLACE_IN_CLOUD_INIT_YML"
diff --git a/templates/cloud-init.yml.tpl b/templates/cloud-init.yml.tpl
new file mode 100644
index 0000000..75f00fd
--- /dev/null
+++ b/templates/cloud-init.yml.tpl
@@ -0,0 +1,61 @@
+#cloud-config
+
+groups:
+  - sysadmins
+
+users:
+  - name: ansibleusr
+    shell: /bin/bash
+    groups: sysadmins
+  - name: theo-agent
+    shell: /bin/false
+    system: true
+
+write_files:
+  - path: /etc/sudoers.d/50-sysadmins
+    owner: root:root
+    permissions: '0440'
+    content: |
+      %sysadmins ALL=(ALL) NOPASSWD: ALL
+  - path: /etc/theo-agent/public.pem
+    owner: root:root
+    permissions: '0644'
+    content: |
+      -----BEGIN PUBLIC KEY-----
+      MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA3gGExRviVBMSL0SZ9Ce9
+      99eWED96iPSKfmjv8Iz3faLmPLKWRDOaxwNdU8Sg48O9IEcAEbgx3jgubNLthiRB
+      U+F5N00ZpFHXByc7h2FxD/JMzxoyxDYSdaQ+JHt+GNYmFA6DYpE6KPaISrWjXgGT
+      KTzN3135qK14ByI0wOUHKESAKPRf/ZMPclwBxEDlfvrn0V1fjBzP1Xbnxv5LFWSl
+      CPGvUA7+msqcHdq0Ep3UPJbnSWO3HhynVevkgj0zVW1GKeps+fa+GFvcyD4N53Ya
+      uGZDefqZgfRxDXrb+vOtiOpVDVvIoicZPt2To9Cd4frpYtNWstDisDoLQBDc6v3p
+      26OmSZlXbCZbEGyknJ7pz/bpOqRpNlElmfGVDKTmX5OAgL2dk1UdDzuxm/WlPXFv
+      SQv9/heZOiYuW7haMHkCELnk8kWnaUxKDC6+Hma8GLVgCP4YjUkpkU7FJD6wksKc
+      U408zgEEBWsk31KtA0BVS8462N24LunQcpB98ZAnL343SkrQcu/n/Nd9DTofInyP
+      NVIt7Sq1C0OqBFic+YoJTgig4mYAIbxGIhai/03IlkVzOf5AuAAX8ddbWX3k5Lnn
+      xFKLuHu71khcDaB1Jn7K22sN5meg+6qAED/xJV7bDXwVyBiknCwMxAiHfz0+kTyI
+      Ugm7jhITOZ0cipYzvPrwIWcCAwEAAQ==
+      -----END PUBLIC KEY-----
+  - path: /usr/sbin/download-install-theo.sh
+    owner: root:root
+    permissions: '0755'
+    content: |
+      #!/bin/bash
+      THEO_AGENT_LATEST=$(curl -L -s -H 'Accept: application/json' https://github.com/theoapp/theo-agent/releases/latest |sed -e 's/.*"tag_name":"\([^"]*\)".*/\1/')
+      sudo curl -L -o /usr/sbin/theo-agent \
+        https://github.com/theoapp/theo-agent/releases/download/$${THEO_AGENT_LATEST}/theo-agent-$(uname -s)-$(uname -m)
+      chmod 755 /usr/sbin/theo-agent
+      /usr/sbin/theo-agent \
+        -install \
+        -verify \
+        -public-key /etc/theo-agent/public.pem \
+        -no-interactive \
+        -sshd-config \
+        -url https://fluidware.authkeys.io \
+        -token ${theo_token}
+        -hostname-prefix fcub-
+      chown -R theo-agent /etc/theo-agent
+      chmod 700 /etc/theo-agent
+      selinuxenabled 2>/dev/null && semanage permissive -a sshd_t
+runcmd:
+  - /usr/sbin/download-install-theo.sh
+  - systemctl restart ssh.service
diff --git a/versions.tf b/versions.tf
new file mode 100644
index 0000000..0940df2
--- /dev/null
+++ b/versions.tf
@@ -0,0 +1,9 @@
+terraform {
+  required_providers {
+    hcloud = {
+      source  = "hetznercloud/hcloud"
+      version = "1.23.0"
+    }
+  }
+  required_version = ">= 0.14"
+}
-- 
GitLab