/ main.tf
main.tf
1 /* DERIVED --------------------------------------*/ 2 locals { 3 stage = var.stage != "" ? var.stage : terraform.workspace 4 dc = "${var.provider_name}-${var.region}" 5 /* always add SSH, WireGuard, and Consul to allowed ports */ 6 open_tcp_ports = concat(["22", "8301"], var.open_tcp_ports) 7 open_udp_ports = concat(["51820", "8301"], var.open_udp_ports) 8 /* tags for the dropplet */ 9 tags = [local.stage, var.group, var.env] 10 tags_sorted = sort(distinct(local.tags)) 11 /* pre-generated list of hostnames */ 12 hostnames = [for i in range(1, var.host_count + 1) : 13 "${var.name}-${format("%02d", i)}.${local.dc}.${var.env}.${local.stage}" 14 ] 15 } 16 /* RESOURCES ------------------------------------*/ 17 18 resource "digitalocean_tag" "host" { 19 for_each = toset(local.tags_sorted) 20 21 name = each.key 22 } 23 24 /* Optional resource when vol_size is set */ 25 resource "digitalocean_volume" "host" { 26 for_each = toset([ for h in local.hostnames : h if var.data_vol_size > 0 ]) 27 28 name = "data-${replace(each.key, ".", "-")}" 29 region = var.region 30 size = var.data_vol_size 31 32 lifecycle { 33 prevent_destroy = true 34 /* We do this to avoid destrying a volume unnecesarily */ 35 ignore_changes = [name] 36 } 37 } 38 39 resource "digitalocean_droplet" "host" { 40 for_each = toset(local.hostnames) 41 42 name = each.key 43 image = var.image 44 region = var.region 45 size = var.type 46 ssh_keys = var.ssh_keys 47 ipv6 = true 48 49 tags = [for tag in digitalocean_tag.host : tag.id] 50 51 /* This can be optional, ugly as hell but it works */ 52 volume_ids = var.data_vol_size > 0 ? [digitalocean_volume.host[each.key].id] : null 53 54 /* Ignore changes in attributes like image */ 55 lifecycle { 56 ignore_changes = [image, ssh_keys] 57 } 58 } 59 60 resource "digitalocean_floating_ip" "host" { 61 for_each = digitalocean_droplet.host 62 63 droplet_id = each.value.id 64 region = each.value.region 65 66 lifecycle { 67 prevent_destroy = false 68 } 69 } 70 71 resource "digitalocean_firewall" "host" { 72 name = "${var.name}.${local.dc}.${var.env}.${local.stage}" 73 droplet_ids = [for name, droplet in digitalocean_droplet.host : droplet.id] 74 75 /* Allow ICMP pings */ 76 inbound_rule { 77 protocol = "icmp" 78 source_addresses = ["0.0.0.0/0", "::/0"] 79 } 80 outbound_rule { 81 protocol = "icmp" 82 destination_addresses = ["0.0.0.0/0", "::/0"] 83 } 84 85 /* TCP */ 86 dynamic "inbound_rule" { 87 iterator = port 88 for_each = local.open_tcp_ports 89 content { 90 protocol = "tcp" 91 port_range = port.value 92 source_addresses = ["0.0.0.0/0", "::/0"] 93 } 94 } 95 96 /* UDP */ 97 dynamic "inbound_rule" { 98 iterator = port 99 for_each = local.open_udp_ports 100 content { 101 protocol = "udp" 102 port_range = port.value 103 source_addresses = ["0.0.0.0/0", "::/0"] 104 } 105 } 106 107 /* Open for all outgoing connections */ 108 dynamic "outbound_rule" { 109 iterator = protocol 110 for_each = ["tcp", "udp"] 111 content { 112 protocol = protocol.value 113 port_range = "all" 114 destination_addresses = ["0.0.0.0/0", "::/0"] 115 } 116 } 117 } 118 119 resource "null_resource" "host" { 120 for_each = digitalocean_droplet.host 121 122 /* Trigger bootstrapping on host or public IP change. */ 123 triggers = { 124 droplet_id = each.value.id 125 #floatin_ip_id = digitalocean_floating_ip.host[count.index].id 126 } 127 128 /* Make sure everything is in place before bootstrapping. */ 129 depends_on = [ 130 digitalocean_volume.host, 131 digitalocean_droplet.host, 132 digitalocean_floating_ip.host, 133 digitalocean_firewall.host, 134 ] 135 136 /* bootstraping access for later Ansible use */ 137 provisioner "ansible" { 138 plays { 139 playbook { 140 file_path = "${path.cwd}/ansible/bootstrap.yml" 141 } 142 143 hosts = [each.value.ipv4_address] 144 groups = [var.group] 145 146 extra_vars = { 147 hostname = each.key 148 ansible_user = var.ssh_user 149 data_center = local.dc 150 stage = local.stage 151 env = var.env 152 } 153 } 154 } 155 } 156 157 resource "cloudflare_record" "host_ipv4" { 158 for_each = digitalocean_droplet.host 159 160 zone_id = var.cf_zone_id 161 name = each.key 162 value = digitalocean_floating_ip.host[each.key].ip_address 163 type = "A" 164 ttl = 3600 165 } 166 167 resource "cloudflare_record" "host_ipv6" { 168 for_each = digitalocean_droplet.host 169 170 zone_id = var.cf_zone_id 171 name = each.key 172 value = digitalocean_droplet.host[each.key].ipv6_address 173 type = "AAAA" 174 ttl = 3600 175 } 176 177 resource "ansible_host" "host" { 178 for_each = digitalocean_droplet.host 179 180 inventory_hostname = each.key 181 182 groups = ["${var.env}.${local.stage}", var.group, local.dc] 183 184 vars = { 185 ansible_host = digitalocean_floating_ip.host[each.key].ip_address 186 hostname = each.key 187 region = each.value.region 188 dns_entry = "${each.key}.${var.domain}" 189 dns_domain = var.domain 190 data_center = local.dc 191 stage = local.stage 192 env = var.env 193 } 194 }