diff --git a/.gitignore b/.gitignore index df38f06..546f7eb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ **/.terraform.lock.hcl -**/.terraform \ No newline at end of file +**/.terraform +**/*.tfplan \ No newline at end of file diff --git a/proxmox/files/talos_cluster_patch.yml b/proxmox/files/talos_cluster_patch.yml new file mode 100644 index 0000000..210377d --- /dev/null +++ b/proxmox/files/talos_cluster_patch.yml @@ -0,0 +1,66 @@ +version: v1alpha1 +debug: false +persist: true + +cluster: + network: + dnsDomain: cluster.local + podSubnets: + - 10.244.0.0/16 + serviceSubnets: + - 10.96.0.0/12 + token: 0rgiji.bwgranqg365c7no5 + secretboxEncryptionSecret: Q4npGGVfneHxxB+CKI1ELnc9bbjvVv/q0raybNAe/lc= + ca: + crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVRDZ0F3SUJBZ0lSQUpOZ0FkaVFxVUovWEdjVjd6clhoeFl3Q2dZSUtvWkl6ajBFQXdJd0ZURVQKTUJFR0ExVUVDaE1LYTNWaVpYSnVaWFJsY3pBZUZ3MHlOakE1TURFeU1URTVNek5hRncwek5qQTRNamt5TVRFNQpNek5hTUJVeEV6QVJCZ05WQkFvVENtdDFZbVZ5Ym1WMFpYTXdXVEFUQmdjcWhrak9QUUlCQmdncWhrak9QUU1CCkJ3TkNBQVRNSE96Mlh0K2xVaERUajRKRU1PRWNlQ2tENkRVZ3lsZzJ3bXFJREpqUE1aMG9VajdwZkVscDUyc28KelJycTlwTXEyK2s2eTlTM21Rc1NqekQ1OWFSam8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXdIUVlEVlIwbApCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWURWUjBPCkJCWUVGS3V1cTN1QUJSSEJoendPNG1WaTRRK2FNaE40TUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUFIdythcXIKbDl4eWpHVUpKTjFyYnJ6c2psZDZSeVhUMk0xVXRVMDdWK1k2QWlBVzJtS1pCSnE3dEk0YmNOMGlNSWo3SFc5NApRT0Q5SnVQbVRRSG5UcGRpN2c9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUtZSnJ2Y1hFU0NLRDlwMURhRjFvLy9Kbm5uWDh5MmtFbnNIQlo1RFFtdEtvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFekJ6czlsN2ZwVklRMDQrQ1JERGhISGdwQStnMUlNcFlOc0pxaUF5WXp6R2RLRkkrNlh4SgphZWRyS00wYTZ2YVRLdHZwT3N2VXQ1a0xFbzh3K2ZXa1l3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= + aggregatorCA: + crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZRENDQVFhZ0F3SUJBZ0lSQUl2RG94RmJUN0h1OHRYenM4SjRZSVl3Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU5qQTVNREV5TVRFNU16TmFGdzB6TmpBNE1qa3lNVEU1TXpOYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVNvUi80QitOR2RzWHJKUUJHRTJNc05BaktZSnBRLzJOc2RzYTYyUDlraG5HTVZzOE1kCnp1cXpFQktOdStLalVNb0xvT3dZUnlCOUQyZXVDcWRFY0hUUW8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGSG9McFRvRE9IQVZKU2ZWMFNnVkpIdEJIb1pPTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDCklRQzA0czY3aXI1VnpaSWdyU1I5bG90WGM5NHp2dkphcGo4QXVXR1VxYjlZcXdJZ0tDVWJFSGZYc2VOY1hqYUoKVmhaQ0ZQWHc3YXY0S0hhZWJwMXo5UEM2eHhJPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUU3QTJ5TTRsVTFPS1EzSFhXTTVLZ3h4a1VKZHorem9zOGhSRmFvREthRUdvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFcUVmK0FmalJuYkY2eVVBUmhOakxEUUl5bUNhVVA5amJIYkd1dGovWklaeGpGYlBESGM3cQpzeEFTamJ2aW8xREtDNkRzR0VjZ2ZROW5yZ3FuUkhCMDBBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= + serviceAccount: + key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKSndJQkFBS0NBZ0VBckFIUXdtRWVka3g3YlNUdzFBMkxpQjNHRzNWVGtDRWRlR2tpdUJ5ZVF0UCtsYVgyCjUvZEFpYnoyNUVCUWhPVDlPZVFJQ1NYbldRUW5abUFFd2lvK3N1Z0hPVlk2QXdqNjhsdkpIMDBoVG5rK0E1VTEKZTZmeU04MUlKOGtwMXhvUTJjamhZQ2ZuZTVVUW5mblN6RHJjblNDYUpUQXNIK1VFUUFuSS8vU01ndjFLbVJhcQprTFpnTXpVYnJlQkdLZnNidEFYRmNBYWtvOVRzOWlNZFAwYk5KWkpCRElFYndBQjRuWGdRS1k5OWtPcVdHRGpSCmwrRXZ3R3pkd0d6d2RMNXJvakx4UGZZSncvMEhRWjRCcG1KWHlaU3N1bDkzSnhCTmxFSHVJbHlUd053alFFNm0KVnYyNjNod3NkWUJsSXR2NUJWdzZ3aDZmMHRiMmNSVEdQMnlPYzJRemg3bXVJU0lNenFaQlRUNEQyVER6Wm1IWAp3Mm55cVN0TXJhV3lSczJnWWk5aXp4OXU1WFIzWTVPVXQrelpOSlVzcFdoQitBZnhJQWxKWTR6NnNieTUyL3JPCnVUWEdQeFlHTzRQNmM5a0FWQ1BqaVVaMFQvYU9ZZU13Q200VmNqNGF6ZGU0eEhJLzNLU0t3MHlwamtLeTZQaFIKQjM5a1FuQlkwOHJyQlJJZHpld1VHb2tXN2ZObTBZWGpHNFgzQ2pGUTBST0pTSnp0MFlSZlZLNXh6Yk1qaS9zeAprdnVnVjA1NWhiTVUwa3BqOThYbTVhYWp5b1UxNVRITklFNDBhWXlnTTlYWXl6ME9jZzdZa0kxVHMrT2laaVZLCkhZOEdyRFltaTJkYUNHdHBFSzlkRjVkNVdDOGY4dEFRdjJWUDFja2tBQWhxZ0JvTzhiL1dSZTVyaFZrQ0F3RUEKQVFLQ0FnQVE1bVpEbk8xMFo5Z1dMWk9jY0I3Q0gxUEdRYUUxMTIwWTlqSi9OcStkaHd6NzJOaEJLeGZNR0xVRQowT1ZnWTB2V2p1Y3p0K1hLeHhmVWJSRjJudFkxZDRyektSdk96Y2h6UE0ydVhjS01ubWkzaXliYnVSRDQ0ajNwClM5Qm90aUxLOVJ2Z3VkYjZlMWRNUUQ0a0N4SC9QYk5yZlRmT28rNm1tSmVIUDNpNVM0ZnpjZWpOQ21BNkU4a2UKREx1MEZ2d2VORlYxWisrdithVVZHM2JkajFncmJYcm03cjRDWDhGNkRsQi9OdDRhV1pRbytkY3RCaWJ1RC93NwpNbkR2TllEbmlUd083NUErMFlydm1XWFYybEtBQjdPdlpaOFVSMXVwK2c3YTBUQUN1bFpiMUladkpHT2lmRS9wCmRSV3F4c1JwTUlwRTVhT2tJSE1MQWdTbStXUm0vSC9wZW92M1g5UURSMFNRYXM5WlNLOVh4R2hlcTNKUDBXSC8KVWFZaFVhb29xb0R0RU1RWGE3VHhLRzNCVE9FU081MDUvWVNTa2V1VW9jR2RyeXJuWnFzd3UwMDJEa3krMTJ2NApiUEl6dEJYT041akx2RUhsNFluL1p0NjVTcWNYQUFObkR4V2hPUkUvWnhCTzcyMjI0NjVBQ1JjUEFVTjF6S1VxCkttdDUwbWJIVGhUNUxsKzRubFpIRmR2RnFlSzZMa2RNeUtvNTVmWW8xL1IzaGk3Q2JibUdTazFra3JxQkhwcmgKTFNsWktuQ2JXUXBDWFpaN3pSdEdyRWp0aHlMZ1hvQXJEKzNTVzZRWDhDNWNvSXpkM2tEaTFUMER2Z1NVa05Mdwoya2tQK3h1R21Hb2hVYU1NSWUxbGVwZGIvaFQxVlJDSmhIdzhScWNhM3hPQ3lHVkpBUUtDQVFFQTJuZDlpdWw4CnFvdUVaWlVDTHVWcnRzNWR1NEEvTW0ycEp5anAzWDh1TktLUTlSSjNQbERvOTV0RkFZMi9PbDEyVmFsYzF0c20KYXp1bm1TOHU1clVyWjZxT1JJdHpoZnBqQVNObi9HTE5RMGtNb2Y5c2lzMEdYR3c1WktOU3lJb0dNM2hvblVsNQpucmhHM1k1R24vUFNLUCtLWVFtUFQ4TUs2RXN5bWMvZ0pDTjkxa1p5cU9RVndSWmljczNCb3Fud3AzOWFLZlZoCmNORk9URjNIbFpPOTZVNGxGeTVURHRHOERsbis1RG5VOHl5clJlUmFaQkN5ZXpLaE83QWZEOUEyV1E4TU5weHIKRE94RWxSVCt6ME5rWWkrN1FEOXhWUlBQZ1hHYk1jZUFEdmpSMkhhU215eXJlTHp2SDZvdG91UUltVDV2SlhLMgp5T05ZR2tQMEp2c1RBUUtDQVFFQXlZNzB6TkJzY0FQdFdyU0doVVZiSFBCN1lpTnBjcWRkU2NiQld5TnJEOHZMCmZJUDBlclkrTnNpaVNsUTY0ZGxVMy9hc2d5QmJKWXFWNWptdmd4YVFaeDJoUlg2VWtheGxlWmZKUURheFJlTjYKSHpMcGgyY04vVEZWTVA1bjZMZjlEUW5ldGd2b1ZlT04xVm1ibDFidE9DVGJhTVpUUG5jb0VsNStMWEFUVEJ1Ngo4OU11ZEVSdjhRbVRQalNhcTZraWJINUdpMlNqc1R3NHRhNHI5Y0YzajBZQ0IzazlCeU5UR2lmQlNmT3d4YkhaCkg5NVBMR3FFTXdUY0MxSFREdm1nVlkyQks3SmlBaVdHSmNmRmN5bHZhN1hzUm1oV0FJNS9VaE1TSDNZQm8wek0KampTeWZKaDE2SkxsUEZpN0NobVJVbmg1Zm1NK2VHa0NRVWpMYU1QcVdRS0NBUUJVZ3JURjc5eVIvTGFTdFRuSworbVRZcXF3SHhHeXVEVTNLSUVHWHRZTWlTV0RaN1BMRDRIaStrQW41dCtPcHFFNEdoU1dzMThuYzRZN0w2enl4CmNwUC8zako2d1pkRzFMMUh5M0tHWjVJUDNVcE53SWswaUd0VmhXZ09Uai9QNDlUU1JGbnp2bUNWemFOY1FaV0EKZXNscG9yRGRLTjJQZFZ2YTBId2FsQmVZNDBIaWlnNEExM3F5M3YzbWUwSFVzdml0VXM2R1BLNGMvbHJUbjlOOQpRVkhOMXJUeWFyRW1vbXVsQVp0Z25oNktrMVVPREhBTEZZcDFyZmhCSnJoZFhhWW9mWEcwRG40NjJZc09RTGp4ClpKaUlndjVrdENZYlN6YVNWR2c4c1hnUVVxQ2FwQVBDazEwaHI3U3hBeHYwVDFJa05jcTVLYzhibHhhVjBvTzQKTUpnQkFvSUJBRFhkT0ZxTDBJN1czZFdCclBsc29YN0lYWkRLZWVHa0RIQXM1M3RkcWJ6Yzc1SmxDUzdLQ1RKRwp4K2dwUGxzZzQyZSsyamRvckRNS0tDa1FmWEVBMUxKWjNzRmRwSUtSbWNTMjVaZDloajV4SGkrOFFXQm9EL3I3CjloSlR5cDBNbmxtZnE1N202NzRZUTgwaVYweElkTGl1VU54ZE05M2JwaC9TbE5HN3c4eHhGTE1ZV3EvRG9rNisKQ2xWelpXbytsdTU3aUlxejZITHk3d3pPY1AwS0tOTm5HQis3TEszOXFMV3lyaHIvUHR4MlBOb2haUE12dkFpTwpZZWU4aERUYVRSalA2aGN0WXJmRUkyZHhUYmtLOTdYSE5rOVNqcmt6VzU4cGJZeHUwdDA3SnVkUitla0dMV3hyCjVVc05RMTdkbWxKNTA2NE5BNkxXSFQ0cElBTHRhSEVDZ2dFQVI0WTB2dTNDbUlZU09taGZaV0ZwdGh0UUJHSUkKemJCYzdDWWd0SU4yZVpDNVA0bUtkRlBFQm9sd1lKcXZ4Z0pSR1VPQ2VCV3J5YmlENFpNMGt2ektmNWpXL3ppNQoybCtGQ1hoUkN1bmZ0WkQxeVdvKytlUDIvTE5EbEs2WTNNOXZzakplSkk2YnVRNjMwU3c0SDBMUmJJanlvVVNvCkEzMzBjdlJiTXpKY2JZaUN6ZWdYK3hoUDZXekRZUlN0OVppZ095WTRldXNpMEU5bkJ2QXBSYUVOQlROMjFLdVMKbmkrODcvZlNBNEprcUJKSWFCOEJISmk4ZWZGVzA1MkdDZXVzUkt5OE5zazc4Q0Q2dnZlWW1YQTZCdUxvSnZqNgpmbWZ1U2ZPdnNoNUtiMy9zR3lmNTM4Q2hTelBKc2RQcGdJNENQaHJFNFU3RVo3c0cxZTFDR29IRExRPT0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K + apiServer: + image: registry.k8s.io/kube-apiserver:v1.36.3 + admissionControl: + - name: PodSecurity + configuration: + apiVersion: pod-security.admission.config.k8s.io/v1alpha1 + defaults: + audit: restricted + audit-version: latest + enforce: baseline + enforce-version: latest + warn: restricted + warn-version: latest + exemptions: + namespaces: + - kube-system + runtimeClasses: [] + usernames: [] + kind: PodSecurityConfiguration + auditPolicy: + apiVersion: audit.k8s.io/v1 + kind: Policy + rules: + - level: Metadata + controllerManager: + image: registry.k8s.io/kube-controller-manager:v1.36.3 + proxy: + image: registry.k8s.io/kube-proxy:v1.36.3 + scheduler: + image: registry.k8s.io/kube-scheduler:v1.36.3 + discovery: + enabled: true + registries: + kubernetes: + disabled: true + service: + disabled: false + etcd: + # ca: + # crt: + # key: +--- +apiVersion: v1alpha1 +kind: HostnameConfig +auto: stable diff --git a/proxmox/files/talos_kubespan_config.yml b/proxmox/files/talos_kubespan_config.yml new file mode 100644 index 0000000..98f09cf --- /dev/null +++ b/proxmox/files/talos_kubespan_config.yml @@ -0,0 +1,5 @@ +# Ref: https://docs.siderolabs.com/talos/latest/reference/configuration/network/kubespanconfig + +apiVersion: v1alpha1 +kind: KubeSpanConfig +enabled: false \ No newline at end of file diff --git a/proxmox/files/talos_machine_patch.yml b/proxmox/files/talos_machine_patch.yml new file mode 100644 index 0000000..8637b5a --- /dev/null +++ b/proxmox/files/talos_machine_patch.yml @@ -0,0 +1,20 @@ +machine: + certSANs: [] + # kubelet: + # image: ghcr.io/siderolabs/kubelet:v1.36.3 + # defaultRuntimeSeccompProfileEnabled: true + # disableManifestsDirectory: true + install: + disk: /dev/sda + image: ghcr.io/siderolabs/installer:v1.13.0 + wipe: false + grubUseUKICmdline: true + features: + diskQuotaSupport: true + kubePrism: + enabled: false + hostDNS: + enabled: true + forwardKubeDNSToHost: false + nodeLabels: + node.kubernetes.io/exclude-from-external-load-balancers: "" \ No newline at end of file diff --git a/proxmox/locals.tf b/proxmox/locals.tf index a8db9e3..f81ede5 100644 --- a/proxmox/locals.tf +++ b/proxmox/locals.tf @@ -3,4 +3,24 @@ locals { # Validate talos_version variable talos_version = [ for version in data.talos_image_factory_versions.this.talos_versions : version if version == var.talos_version][0] cluster_endpoint = "https://${var.cluster_name}:6443" + machine_config_patches = { for guest in var.vm_guests: guest.name => [ + templatefile("${path.root}/templates/talos_hostname_config.yml.tftpl", { + hostname = guest.name + }), + templatefile("${path.root}/templates/talos_link_config.yml.tftpl", { + ipv4_address = guest.ipv4_address + ipv4_subnet_mask = var.ipv4_subnet_mask + ipv4_gateway = var.ipv4_gateway + }), + templatefile("${path.root}/templates/talos_resolver_config.yml.tftpl", { + ipv4_nameservers = var.ipv4_nameservers + ipv6_nameservers = var.ipv6_nameservers + search_domains = var.search_domains + }), + templatefile("${path.root}/templates/talos_timesync_config.yml.tftpl", { + ipv4_ntp_servers = var.ipv4_ntp_servers + ipv6_ntp_servers = var.ipv6_ntp_servers + }), + file("${path.root}/files/talos_kubespan_config.yml") + ] if contains(["controlplane", "worker"], guest.role)} } \ No newline at end of file diff --git a/proxmox/network.auto.tfvars b/proxmox/network.auto.tfvars new file mode 100644 index 0000000..82d1007 --- /dev/null +++ b/proxmox/network.auto.tfvars @@ -0,0 +1,15 @@ +ipv4_gateway = "10.96.10.254" +ipv4_subnet_mask = 24 +ipv6_gateway = null +ipv4_nameservers = [ + {address = "10.96.10.254"} +] +ipv6_ntp_servers = [] +search_domains = [ + "balsillie.net", + "balsillie.house" +] +ipv4_ntp_servers = [ + "10.96.10.254" +] +ipv6_nameservers = [] \ No newline at end of file diff --git a/proxmox/outputs.tf b/proxmox/outputs.tf index d4e1840..b7e3482 100644 --- a/proxmox/outputs.tf +++ b/proxmox/outputs.tf @@ -12,5 +12,5 @@ output "talos_schematic_id" { output "debug" { description = "For debugging resource or data key values" value = data.talos_machine_configuration.this["cp01"] - sensitive = false + sensitive = true } \ No newline at end of file diff --git a/proxmox/talos.tf b/proxmox/talos.tf index c6d67dd..989660e 100644 --- a/proxmox/talos.tf +++ b/proxmox/talos.tf @@ -22,7 +22,7 @@ data "talos_machine_configuration" "this" { for_each = { for guest in var.vm_guests: guest.name => guest if contains(["controlplane", "worker"], guest.role) } cluster_endpoint = local.cluster_endpoint cluster_name = var.cluster_name - config_patches = [] + config_patches = local.machine_config_patches[each.key] docs = false examples = false kubernetes_version = var.kubernetes_version diff --git a/proxmox/templates/talos_hostname_config.yml.tftpl b/proxmox/templates/talos_hostname_config.yml.tftpl new file mode 100644 index 0000000..dfc579a --- /dev/null +++ b/proxmox/templates/talos_hostname_config.yml.tftpl @@ -0,0 +1,6 @@ +# Ref: https://docs.siderolabs.com/talos/latest/reference/configuration/network/hostnameconfig + +apiVersion: v1alpha1 +kind: HostnameConfig +hostname: ${hostname} +auto: off \ No newline at end of file diff --git a/proxmox/templates/talos_link_config.yml.tftpl b/proxmox/templates/talos_link_config.yml.tftpl new file mode 100644 index 0000000..c43940f --- /dev/null +++ b/proxmox/templates/talos_link_config.yml.tftpl @@ -0,0 +1,12 @@ +# Ref: https://docs.siderolabs.com/talos/latest/reference/configuration/network/linkconfig + +apiVersion: v1alpha1 +kind: LinkConfig +name: enp0s1 # Default link position assigned by proxmox +up: true +mtu: 1500 +addresses: + - address: ${ipv4_address}/${ipv4_subnet_mask} +routes: + - destination: 0.0.0.0/0 + gateway: ${ipv4_gateway} diff --git a/proxmox/templates/talos_resolver_config.yml.tftpl b/proxmox/templates/talos_resolver_config.yml.tftpl new file mode 100644 index 0000000..0fb8f7e --- /dev/null +++ b/proxmox/templates/talos_resolver_config.yml.tftpl @@ -0,0 +1,8 @@ +# Ref: https://docs.siderolabs.com/talos/latest/reference/configuration/network/resolverconfig + +apiVersion: v1alpha1 +kind: ResolverConfig +nameservers: ${jsonencode(concat(ipv4_nameservers, ipv6_nameservers))} +searchDomains: + disableDefault: true + domains: ${jsonencode(search_domains)} \ No newline at end of file diff --git a/proxmox/templates/talos_timesync_config.yml.tftpl b/proxmox/templates/talos_timesync_config.yml.tftpl new file mode 100644 index 0000000..eeb3539 --- /dev/null +++ b/proxmox/templates/talos_timesync_config.yml.tftpl @@ -0,0 +1,8 @@ +# Ref: https://docs.siderolabs.com/talos/latest/reference/configuration/network/timesyncconfig + +apiVersion: v1alpha1 +kind: TimeSyncConfig +enabled: true +# bootTimeout: "infinity" +ntp: + servers: ${jsonencode(concat(ipv4_ntp_servers, ipv6_ntp_servers))} \ No newline at end of file diff --git a/proxmox/variables.tf b/proxmox/variables.tf index 990dd85..a50bf7c 100644 --- a/proxmox/variables.tf +++ b/proxmox/variables.tf @@ -19,4 +19,40 @@ variable "cluster_name" { variable "kubernetes_version" { type = string +} + +variable "ipv4_gateway" { + type = string +} + +variable "ipv6_gateway" { + type = string +} + +variable "ipv4_ntp_servers" { + type = list(string) +} + +variable "ipv6_ntp_servers" { + type = list(string) +} + +variable "ipv4_subnet_mask" { + type = number +} + +variable "search_domains" { + type = list(string) +} + +variable "ipv4_nameservers" { + type = list(object({ + address = string + })) +} + +variable "ipv6_nameservers" { + type = list(object({ + address = string + })) } \ No newline at end of file