/ main.tf
main.tf
1 locals { 2 stage = var.stage != "" ? var.stage : terraform.workspace 3 4 /* we're prepending "eu-" to the location to keep the same format as our 5 * other tf provider modules (aws, gcp, do ,...) 6 * all hetzner cloud data centers are in the EU */ 7 dc = "${var.provider_name}-eu-${var.location}" 8 9 /* Got to add some default groups. */ 10 groups = distinct([local.dc, "${var.env}.${local.stage}", var.group]) 11 12 /* example: stable-large-01.he-eu-hel1.nimbus.default */ 13 host_suffix = "${local.dc}.${var.env}.${local.stage}" 14 15 /* always add SSH, WireGuard, and Consul to allowed ports */ 16 open_tcp_ports = concat(["22", "8301"], var.open_tcp_ports) 17 open_udp_ports = concat(["51820", "8301"], var.open_udp_ports) 18 19 /* pre-generated list of hostnames */ 20 hostnames = toset([for i in range(1, var.host_count + 1) : 21 "${var.name}-${format("%02d", i)}.${local.host_suffix}" 22 ]) 23 } 24 25 resource "hcloud_firewall" "host" { 26 name = "${var.name}.${local.host_suffix}" 27 28 /* TCP */ 29 dynamic "rule" { 30 for_each = local.open_tcp_ports 31 iterator = port 32 content { 33 direction = "in" 34 protocol = "tcp" 35 port = port.value 36 source_ips = [ 37 "0.0.0.0/0", 38 "::/0" 39 ] 40 } 41 } 42 43 /* UDP */ 44 dynamic "rule" { 45 for_each = local.open_udp_ports 46 iterator = port 47 content { 48 direction = "in" 49 protocol = "udp" 50 port = port.value 51 source_ips = [ 52 "0.0.0.0/0", 53 "::/0" 54 ] 55 } 56 } 57 } 58 59 resource "hcloud_server" "host" { 60 for_each = local.hostnames 61 name = each.key 62 image = var.image 63 server_type = var.type 64 location = var.location 65 ssh_keys = var.ssh_keys 66 firewall_ids = [hcloud_firewall.host.id] 67 68 /* floating IPs need to be assigned manually */ 69 user_data = templatefile("${path.module}/user-data/floating_ip.sh", { 70 floating_ip = hcloud_floating_ip.host[each.key].ip_address 71 }) 72 73 /* Ignore changes in attributes like image */ 74 lifecycle { 75 ignore_changes = [image, ssh_keys, user_data] 76 } 77 78 /* wait for cloud-init (ensures instance is fully booted before moving on) 79 * if we don't wait the ansible provisioner will fail with connection errors 80 */ 81 provisioner "remote-exec" { 82 connection { 83 user = "root" 84 host = self.ipv4_address 85 } 86 inline = [ 87 "echo 'Waiting for cloud-init to complete...'", 88 "cloud-init status --wait > /dev/null", 89 "echo 'Completed cloud-init!'", 90 ] 91 } 92 93 /* bootstraping access for later Ansible use */ 94 provisioner "ansible" { 95 plays { 96 playbook { 97 file_path = "${path.cwd}/ansible/bootstrap.yml" 98 } 99 100 hosts = [self.ipv4_address] 101 groups = [var.group] 102 103 extra_vars = { 104 hostname = self.name 105 data_center = local.dc 106 stage = local.stage 107 env = var.env 108 ansible_ssh_user = var.ssh_user 109 } 110 } 111 } 112 } 113 114 resource "hcloud_floating_ip" "host" { 115 for_each = local.hostnames 116 type = "ipv4" 117 home_location = var.location 118 119 lifecycle { 120 prevent_destroy = true 121 } 122 } 123 124 resource "hcloud_floating_ip_assignment" "host" { 125 for_each = local.hostnames 126 floating_ip_id = hcloud_floating_ip.host[each.key].id 127 server_id = hcloud_server.host[each.key].id 128 } 129 130 /* Optional resource when data_vol_size is set */ 131 resource "hcloud_volume" "host" { 132 for_each = toset([for h in local.hostnames : h if var.data_vol_size != 0]) 133 name = "data-${replace(each.key, ".", "-")}" 134 server_id = hcloud_server.host[each.key].id 135 size = var.data_vol_size 136 137 lifecycle { 138 prevent_destroy = true 139 /* We do this to avoid destrying a volume unnecesarily */ 140 ignore_changes = [name] 141 } 142 } 143 144 resource "cloudflare_record" "host" { 145 for_each = local.hostnames 146 zone_id = var.cf_zone_id 147 name = hcloud_server.host[each.key].name 148 value = hcloud_floating_ip.host[each.key].ip_address 149 type = "A" 150 ttl = 3600 151 } 152 153 resource "ansible_host" "host" { 154 for_each = local.hostnames 155 inventory_hostname = hcloud_server.host[each.key].name 156 157 groups = local.groups 158 159 vars = { 160 ansible_host = hcloud_floating_ip.host[each.key].ip_address 161 162 hostname = hcloud_server.host[each.key].name 163 region = hcloud_server.host[each.key].location 164 165 dns_entry = "${hcloud_server.host[each.key].name}.${var.domain}" 166 dns_domain = var.domain 167 data_center = local.dc 168 stage = local.stage 169 env = var.env 170 } 171 }