resource "kubernetes_namespace" "website" { metadata { annotations = { name = "website" } labels = { name = "website" } name = "website" } } resource "kubernetes_service_account" "website_service_account" { metadata { name = "website-app-sa" namespace = kubernetes_namespace.website.metadata[0].name } automount_service_account_token = false } resource "kubernetes_service" "website" { for_each = var.websites metadata { name = format("website-%s", replace(each.key, ".", "-")) namespace = kubernetes_namespace.website.metadata[0].name } spec { port { name = "http" port = 80 target_port = "http" } dynamic "port" { for_each = each.value.tls ? [1] : [] content { name = "https" port = 443 target_port = "https" } } selector = { app = format("website-%s", replace(each.key, ".", "-")) } type = "ClusterIP" cluster_ip = "None" } } resource "kubernetes_manifest" "internal_cert" { for_each = { for k, v in var.websites : k => v if v.tls == true } manifest = { apiVersion = "cert-manager.io/v1" kind = "Certificate" metadata = { name = format("website-%s-tls-internal", replace(each.key, ".", "-")) namespace = kubernetes_namespace.website.metadata[0].name } spec = { secretName = format("website-%s-tls-internal", replace(each.key, ".", "-")) issuerRef = { name = each.value.issuer kind = "ClusterIssuer" } commonName = each.value.domain_name dnsNames = each.value.fqdn } } } resource "kubernetes_config_map" "nginx_config" { metadata { name = "nginx-config" namespace = kubernetes_namespace.website.metadata[0].name } data = { "nginx.conf" = local.nginx_config } } resource "kubernetes_config_map" "nginx_default_config" { for_each = var.websites metadata { name = format("website-%s-default", replace(each.key, ".", "-")) namespace = kubernetes_namespace.website.metadata[0].name } data = { "default.conf" = coalesce(each.value.config, templatefile("${path.module}/resources/conf.d/default.conf.tpl", { tls = each.value.tls fqdn = each.value.domain_name custom_snippet = each.value.custom_snippet })) } } resource "kubernetes_ingress_v1" "website_ingress" { for_each = var.websites metadata { name = "website-ingress" namespace = kubernetes_namespace.website.metadata[0].name annotations = merge({ "kubernetes.io/ingress.class" = "public" "cert-manager.io/cluster-issuer" = "letsencrypt-prod" }, each.value.tls ? { "nginx.org/ssl-services" = format("website-%s", replace(each.key, ".", "-")) "nginx.org/ssl-redirect" = "true" "nginx.org/redirect-to-https" = "true" "nginx.ingress.kubernetes.io/backend-protocol" = "HTTPS" } : {} ) } spec { tls { hosts = each.value.fqdn secret_name = format("website-%s-tls", replace(each.key, ".", "-")) } dynamic "rule" { for_each = each.value.fqdn content { host = rule.value http { path { backend { service { name = format("website-%s", replace(each.key, ".", "-")) port { name = each.value.tls ? "https" : "http" } } } } } } } } } resource "kubernetes_deployment" "website" { for_each = var.websites metadata { name = "website-app" namespace = kubernetes_namespace.website.metadata[0].name } spec { selector { match_labels = { app = format("website-%s", replace(each.key, ".", "-")) } } strategy { type = "RollingUpdate" } template { metadata { labels = { app = format("website-%s", replace(each.key, ".", "-")) } annotations = { "checksum/nginx" = local.nginx_config_checksum "checksum/default" = sha256(coalesce(each.value.config, templatefile("${path.module}/resources/conf.d/default.conf.tpl", { tls = each.value.tls fqdn = each.value.domain_name custom_snippet = each.value.custom_snippet })) ) } } spec { service_account_name = kubernetes_service_account.website_service_account.metadata[0].name container { name = "website" image = "nginx:${var.tag}" image_pull_policy = "Always" resources { requests = { memory = "100Mi" } } readiness_probe { http_get { path = "/" port = 8080 } initial_delay_seconds = 5 period_seconds = 10 } liveness_probe { http_get { path = "/" port = 8080 } initial_delay_seconds = 5 period_seconds = 10 } security_context { run_as_user = 1000 run_as_group = 1000 allow_privilege_escalation = false read_only_root_filesystem = true } port { container_port = 8080 name = "http" } dynamic "port" { for_each = each.value.tls ? [443] : [] content { container_port = 8443 name = "https" } } volume_mount { name = "nginx" mount_path = "/etc/nginx/nginx.conf" sub_path = "nginx.conf" read_only = true } volume_mount { name = "default" mount_path = "/etc/nginx/conf.d/default.conf" sub_path = "default.conf" read_only = true } volume_mount { name = "tmp" mount_path = "/tmp" } volume_mount { name = "website" mount_path = "/tmp/site" read_only = true } dynamic "volume_mount" { for_each = each.value.tls ? [443] : [] content { name = "tls" mount_path = "/etc/ssl/certs/private" read_only = true } } } volume { name = "nginx" config_map { name = kubernetes_config_map.nginx_config.metadata[0].name } } volume { name = "default" config_map { name = kubernetes_config_map.nginx_default_config[each.key].metadata[0].name } } volume { name = "tmp" empty_dir { medium = "Memory" } } volume { name = "website" host_path { path = format("%s/%s", local.websites_path, each.key) } } dynamic "volume" { for_each = each.value.tls ? [443] : [] content { name = "tls" secret { secret_name = kubernetes_manifest.internal_cert[each.key].manifest["metadata"]["name"] items { key = "tls.crt" path = "tls.crt" } items { key = "tls.key" path = "tls.key" } } } } } } } lifecycle { ignore_changes = [spec[0].template[0].metadata[0].annotations["kubectl.kubernetes.io/restartedAt"]] } }