commit 87ebb350167b27677faaca7f3b3916fa25d8915d Author: Robert Kaussow Date: Thu Sep 1 21:43:01 2022 +0200 initial commit diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..c9ba5c6 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,37 @@ +--- +kind: pipeline +name: check + +platform: + os: linux + arch: amd64 + +steps: + - name: whitespace + pull: always + image: thegeeklab/alpine-tools + commands: + - git diff-tree --check $(git hash-object -t tree /dev/null) HEAD + + - name: terraform + pull: always + image: jmccann/drone-terraform:8 + settings: + actions: + - validate + tf_version: 1.2.0 + +node: + zone: mgmt + +trigger: + ref: + - refs/heads/main + - refs/tags/** + - refs/pull/** + +--- +kind: signature +hmac: ec825d5879e3dc1d26bdc619143cf2ea41d5183c2d7b892498b21103379675c2 + +... diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..bf61628 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +/terraform/.terraform + +*.retry +*.tfvars +*.tfstate +.terraform.lock.hcl + +.vaultpasswd +.envrc + +/galaxy diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..ef05acb --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +.drone* diff --git a/.renovaterc.json b/.renovaterc.json new file mode 100644 index 0000000..4ac4f8d --- /dev/null +++ b/.renovaterc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["github>thegeeklab/renovate-presets:ansible"] +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3812eb4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Robert Kaussow + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a07c341 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# hcloud-server-tf + +[![Build Status](https://drone.rknet.org/api/badges/infra/hcloud_server_tf/status.svg)](https://drone.rknet.org/infra/hcloud_server_tf) + +Terraform module to provision Hetzner Cloud servers. + +## Usage + +This [Terraform module](https://www.terraform.io/docs/language/modules/syntax.html) is used in our deployment repos to avoid duplicate resource definitions and simplify maintenance. + +## License + +This project is licensed under the MIT License - see the [LICENSE](https://github.com/ansible/galaxy/blob/main/LICENSE) file for details. diff --git a/data.tf b/data.tf new file mode 100644 index 0000000..30f3bac --- /dev/null +++ b/data.tf @@ -0,0 +1,8 @@ +data "cloudflare_zones" "zones" { + for_each = toset(try(var.cloudflare_zones, [])) + + filter { + name = each.key + status = "active" + } +} diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..c449faf --- /dev/null +++ b/main.tf @@ -0,0 +1,133 @@ +locals { + zones = { + for zone in try(data.cloudflare_zones.zones, []) : zone.zones[0].name => zone.zones[0].id + } +} + +locals { + server_volumes = flatten([ + for server_key, server in var.server : [ + for volume_key, volume in try(server.volumes, []) : { + volume_name = volume.name + volume_size = volume.size + server_id = hcloud_server.server[server.name].id + } + ] + ]) +} + +locals { + server_domains = flatten([ + for server_key, server in var.server : [ + for domain_key, domain in try(server.domains, []) : { + record_id = try(domain.id, domain.name) + record_name = domain.name + record_type = domain.type + record_value = domain.value + record_ttl = try(domain.ttl, 1) + zone_id = local.zones[try(domain.zone_name, var.cloudflare_default_zone)] + } + ] + ]) +} + +resource "hcloud_placement_group" "group" { + for_each = { for key, row in var.server : row.name => row if contains(keys(row), "placement_group") } + + name = each.value.placement_group + type = "spread" +} + +resource "hcloud_server" "server" { + for_each = { for row in var.server : row.name => row } + depends_on = [hcloud_ssh_key.key] + + name = each.value.name + server_type = try(each.value.type, "cx11") + image = try(each.value.image, "rocky-9") + datacenter = try(each.value.datacenter, "fsn1-dc14") + ssh_keys = var.server_keys + backups = try(each.value.backups, true) + keep_disk = try(each.value.keep_disk, false) + placement_group_id = try(hcloud_placement_group.group[each.value.placement_group].id, null) + + lifecycle { + ignore_changes = [ssh_keys] + } + + labels = merge( + { + provisioner = "ansible" + project = var.hcloud_project + deploygroup = try(each.value.deploygroup, "1") + }, + { + for index, service in try(each.value.services, []) : "service_${index + 1}" => service + }, + { + for index, group in try(each.value.groups, []) : "group_${index + 1}" => group + }, + try(each.value.labels, {}) + ) +} + +resource "hcloud_volume" "volume" { + for_each = { for row in local.server_volumes : row.volume_name => row } + + name = each.value.volume_name + size = each.value.volume_size + server_id = each.value.server_id +} + +resource "hcloud_ssh_key" "key" { + for_each = { for row in toset(var.ssh_keys) : element(split(" ", row), 2) => row } + + name = each.key + public_key = "${element(split(" ", each.value), 0)} ${element(split(" ", each.value), 1)}" +} + +resource "hcloud_rdns" "serverv4" { + for_each = { for row in var.server : row.name => row } + + server_id = hcloud_server.server[each.value.name].id + ip_address = hcloud_server.server[each.value.name].ipv4_address + dns_ptr = "${each.value.name}.${local.zones[try(each.value.dns_zone, var.cloudflare_default_zone)]}" +} + +resource "hcloud_rdns" "serverv6" { + for_each = { for row in var.server : row.name => row } + + server_id = hcloud_server.server[each.value.name].id + ip_address = hcloud_server.server[each.value.name].ipv6_address + dns_ptr = "${each.value.name}.${local.zones[try(each.value.dns_zone, var.cloudflare_default_zone)]}" +} + +resource "cloudflare_record" "serverv4" { + for_each = { for row in var.server : row.name => row } + + zone_id = local.zones[try(each.value.dns_zone, var.cloudflare_default_zone)] + name = each.value.name + value = hcloud_server.server[each.value.name].ipv4_address + type = "A" + ttl = 1 +} + +resource "cloudflare_record" "serverv6" { + for_each = { for row in var.server : row.name => row } + + zone_id = local.zones[try(each.value.dns_zone, var.cloudflare_default_zone)] + name = each.value.name + value = hcloud_server.server[each.value.name].ipv6_address + type = "AAAA" + ttl = 1 +} + +resource "cloudflare_record" "record" { + for_each = { for row in local.server_domains : row.record_id => row } + + zone_id = each.value.zone_id + name = each.value.record_name + value = each.value.record_value + type = each.value.record_type + ttl = each.value.record_ttl +} diff --git a/outputs.tf b/outputs.tf new file mode 100644 index 0000000..a8693a9 --- /dev/null +++ b/outputs.tf @@ -0,0 +1,9 @@ +output "output" { + value = { + for server_key, server in hcloud_server.server : server_key => server + } +} + +output "zones" { + value = local.zones +} diff --git a/providers.tf b/providers.tf new file mode 100644 index 0000000..8155974 --- /dev/null +++ b/providers.tf @@ -0,0 +1,7 @@ +provider "hcloud" { + token = var.hcloud_token +} + +provider "cloudflare" { + api_token = var.cloudflare_api_token +} diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..025efed --- /dev/null +++ b/variables.tf @@ -0,0 +1,31 @@ +variable "hcloud_token" { + type = string +} + +variable "hcloud_project" { + type = string +} + +variable "cloudflare_api_token" { + type = string +} + +variable "cloudflare_zones" { + type = list(string) +} + +variable "server" { + default = [] +} + +variable "cloudflare_default_zone" { + type = string +} + +variable "server_keys" { + type = list(string) +} + +variable "ssh_keys" { + type = list(string) +} diff --git a/versions.tf b/versions.tf new file mode 100644 index 0000000..a4fbcf8 --- /dev/null +++ b/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_providers { + cloudflare = { + source = "cloudflare/cloudflare" + } + hcloud = { + source = "hetznercloud/hcloud" + } + } +}