diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..81e4230
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/debug/*
+/.idea
diff --git a/default.conf b/default.conf
new file mode 100644
index 0000000..a40db9f
--- /dev/null
+++ b/default.conf
@@ -0,0 +1,19 @@
+resolver 127.0.0.11 ipv6=off valid=1s;
+
+server {
+ listen 80;
+ listen [::]:80;
+ #listen 443 ssl;
+ #listen [::]:443 ssl;
+ server_name localhost;
+
+ location / {
+ root /usr/share/nginx/html;
+ index index.html index.htm;
+ }
+
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root /usr/share/nginx/html;
+ }
+}
diff --git a/default_page.tf b/default_page.tf
new file mode 100644
index 0000000..649a096
--- /dev/null
+++ b/default_page.tf
@@ -0,0 +1,19 @@
+locals {
+ default_page = "
Hello, World!
"
+}
+resource "docker_config" "default_page" {
+ name = "${var.service_name}.index.html-${substr(sha1(local.default_page), 0, 4)}"
+ data = base64encode(local.default_page)
+}
+resource "local_file" "default_page" {
+ content = base64decode(docker_config.default_page.data)
+ filename = "${path.root}/.debug/nginx/index.html"
+}
+resource "docker_config" "default_conf" {
+ name = "${var.service_name}.default.conf-${substr(sha1(file("${path.module}/default.conf")), 0, 4)}"
+ data = base64encode(file("${path.module}/default.conf"))
+}
+resource "local_file" "default_conf" {
+ content = base64decode(docker_config.default_conf.data)
+ filename = "${path.root}/.debug/nginx/default.conf"
+}
diff --git a/inputs.tf b/inputs.tf
new file mode 100644
index 0000000..9e4b5bf
--- /dev/null
+++ b/inputs.tf
@@ -0,0 +1,22 @@
+variable "service_name" {
+ type = string
+ default = "nginx"
+}
+variable "configs" {
+ type = list(object({
+ file = string
+ id = string
+ name = string
+ }))
+}
+variable "networks" {
+ type = list(object({
+ name = string
+ id = string
+ }))
+}
+variable "replicas" {
+ type = number
+ default = 2
+ description = "The number of instances to deploy"
+}
diff --git a/nginx-site-available/basic-auth.tf b/nginx-site-available/basic-auth.tf
new file mode 100644
index 0000000..bbb9e71
--- /dev/null
+++ b/nginx-site-available/basic-auth.tf
@@ -0,0 +1,11 @@
+# Auth file
+resource "docker_config" "auth" {
+ count = var.basic_auth != null ? 1 : 0
+ name = join(".", [var.config_prefix, "auth", var.hostname, random_id.config_instance.id])
+ data = base64encode(local.auth)
+}
+resource "local_file" "auth" {
+ count = var.basic_auth != null ? 1 : 0
+ content = local.auth
+ filename = "${path.root}/.debug/nginx/${local.filenames.auth}"
+}
diff --git a/nginx-site-available/cert.tf b/nginx-site-available/cert.tf
new file mode 100644
index 0000000..f691e86
--- /dev/null
+++ b/nginx-site-available/cert.tf
@@ -0,0 +1,20 @@
+resource "docker_config" "certificate" {
+ count = var.certificate != null ? 1 : 0
+ name = join(".", [var.config_prefix, "crt", var.hostname, random_id.config_instance.id])
+ data = base64encode("${var.certificate.certificate_pem}${var.certificate.issuer_pem}")
+}
+resource "local_file" "certificate" {
+ count = var.certificate != null ? 1 : 0
+ content = local.cert_public
+ filename = "${path.root}/.debug/nginx/${local.filenames.certificate}"
+}
+resource "docker_config" "certificate_key" {
+ count = var.certificate != null ? 1 : 0
+ name = join(".", [var.config_prefix, "key", var.hostname, random_id.config_instance.id])
+ data = base64encode(local.cert_private)
+}
+resource "local_file" "certificate_key" {
+ count = var.certificate != null ? 1 : 0
+ content = var.certificate.private_key_pem
+ filename = "${path.root}/.debug/nginx/${local.filenames.certificate_key}"
+}
diff --git a/nginx-site-available/config.tf b/nginx-site-available/config.tf
new file mode 100644
index 0000000..925086a
--- /dev/null
+++ b/nginx-site-available/config.tf
@@ -0,0 +1,70 @@
+locals {
+ auth = var.basic_auth != null ? "${var.basic_auth.username}:${var.basic_auth.password}" : null
+ config = templatefile("${path.module}/nginx_template.conf", {
+ hostname = var.hostname
+ service_name = var.service_name
+ http_port = var.http_port
+ https_port = var.https_port
+ upstream_host = var.upstream_host
+ enable_ssl = var.certificate != null
+ certificate = var.certificate
+ basic_auth = var.basic_auth
+ auth_file = var.basic_auth != null ? "${var.hostname}-auth.conf" : ""
+ allow_non_ssl = var.allow_non_ssl
+ redirect_non_ssl = var.redirect_non_ssl
+ timeout_seconds = var.timeout_seconds
+ host_override = var.host_override
+ extra_upstreams = var.extra_upstreams
+ extra_locations = var.extra_locations
+ })
+ cert_public = "${var.certificate.issuer_pem}${var.certificate.certificate_pem}"
+ cert_private = var.certificate.private_key_pem
+ filenames = {
+ nginx = "${var.hostname}.conf"
+ auth = "${var.hostname}.auth"
+ certificate_key = "${var.hostname}.key"
+ certificate = "${var.hostname}.crt"
+ }
+ files = [for f in [
+ {
+ file = local.filenames.nginx
+ name = docker_config.nginx_site_available.name
+ id = docker_config.nginx_site_available.id
+ },
+ var.basic_auth != null ? {
+ file = local.filenames.auth
+ name = docker_config.auth[0].name
+ id = docker_config.auth[0].id
+ } : null,
+ var.certificate != null ? {
+ file = local.filenames.certificate
+ name = docker_config.certificate[0].name
+ id = docker_config.certificate[0].id
+ } : null,
+ var.certificate != null ? {
+ file = local.filenames.certificate_key
+ name = docker_config.certificate_key[0].name
+ id = docker_config.certificate_key[0].id
+ } : null
+ ] : f if f != null]
+}
+
+# Nginx config
+resource "random_id" "config_instance" {
+ byte_length = 4
+ keepers = {
+ config : local.config,
+ auth : local.auth,
+ cert_public : local.cert_public,
+ cert_private : local.cert_private,
+ }
+}
+resource "docker_config" "nginx_site_available" {
+ name = join(".", [var.config_prefix, "conf", var.hostname, random_id.config_instance.id])
+ data = base64encode(local.config)
+}
+
+resource "local_file" "nginx_site_available" {
+ filename = "${path.root}/.debug/nginx/${local.filenames.nginx}"
+ content = local.config
+}
diff --git a/nginx-site-available/inputs.tf b/nginx-site-available/inputs.tf
new file mode 100644
index 0000000..c3b18e7
--- /dev/null
+++ b/nginx-site-available/inputs.tf
@@ -0,0 +1,67 @@
+variable "hostname" {
+ type = string
+ description = "The hostname of the server"
+}
+variable "service_name" {
+ type = string
+ description = "The name of the service"
+}
+variable "upstream_host" {
+ type = string
+ description = "The host uri of the upstream server"
+}
+variable "certificate" {
+ type = object({
+ private_key_pem = string
+ certificate_pem = string
+ issuer_pem = string
+ })
+ default = null
+}
+variable "basic_auth" {
+ type = object({
+ username = string
+ password = string
+ })
+ default = null
+}
+variable "allow_non_ssl" {
+ type = bool
+ default = false
+}
+variable "redirect_non_ssl" {
+ type = bool
+ default = true
+}
+variable "timeout_seconds" {
+ type = number
+ default = 10
+}
+variable "http_port" {
+ type = number
+ default = 80
+}
+variable "https_port" {
+ type = number
+ default = 443
+}
+variable "host_override" {
+ type = string
+ default = null
+}
+variable "config_prefix" {
+ type = string
+ default = "nginx"
+}
+
+variable "extra_upstreams" {
+ type = list(object({
+ name = string
+ servers = list(string)
+ }))
+ default = []
+}
+variable "extra_locations" {
+ type = string
+ default = ""
+}
diff --git a/nginx-site-available/nginx_template.conf b/nginx-site-available/nginx_template.conf
new file mode 100644
index 0000000..463cbc5
--- /dev/null
+++ b/nginx-site-available/nginx_template.conf
@@ -0,0 +1,83 @@
+upstream ${service_name} {
+ least_conn;
+ server ${upstream_host};
+}
+%{for upstream in extra_upstreams~}
+upstream ${upstream.name} {
+ least_conn;
+%{for server in upstream.servers~}
+ server ${server};
+%{endfor~}
+}
+%{endfor~}
+
+%{if !allow_non_ssl~}
+server {
+ # Redirect non-ssl to ssl
+ listen ${http_port};
+ listen [::]:${http_port};
+ server_name ${hostname};
+ return 301 https://$host$request_uri;
+}
+%{endif~}
+
+server {
+%{if allow_non_ssl~}
+ # Non-SSL Traffic is allowed
+ listen ${http_port~}
+ listen [::]:${http_port};
+%{endif~}
+ # SSL Traffic is allowed
+ listen ${https_port} ssl;
+ listen [::]:${https_port} ssl;
+ server_name ${hostname};
+ access_log /var/log/nginx/${hostname}.access.log;
+ error_log /var/log/nginx/${hostname}.error.log;
+
+%{if enable_ssl~}
+ ssl_certificate /etc/nginx/conf.d/${hostname}.crt;
+ ssl_certificate_key /etc/nginx/conf.d/${hostname}.key;
+ # ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+ # ssl_ciphers HIGH:!aNULL:!MD5;
+%{endif~}
+
+ client_max_body_size 0;
+
+ location / {
+%{if host_override != null~}
+ proxy_set_header Host ${host_override};
+%{else~}
+ proxy_set_header Host $host;
+%{endif~}
+
+ # Server to send the request on to
+ proxy_pass http://${service_name};
+
+ # Standard headers setting origin data
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+%{if basic_auth != null~}
+ # Http Basic Auth
+ auth_basic "closed site";
+ auth_basic_user_file sites-enabled/${auth_file};
+%{endif~}
+
+ # WebSocket support
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "Upgrade";
+ proxy_cache_bypass $http_upgrade;
+ proxy_buffering off;
+ proxy_set_header Origin "";
+
+ # Proxy timeouts
+ proxy_read_timeout ${timeout_seconds};
+ proxy_connect_timeout ${timeout_seconds};
+ proxy_send_timeout ${timeout_seconds};
+ }
+
+${extra_locations}
+}
+
diff --git a/nginx-site-available/outputs.tf b/nginx-site-available/outputs.tf
new file mode 100644
index 0000000..967254f
--- /dev/null
+++ b/nginx-site-available/outputs.tf
@@ -0,0 +1,6 @@
+output "files" {
+ value = local.files
+}
+output "hostname" {
+ value = var.hostname
+}
diff --git a/nginx-site-available/terraform.tf b/nginx-site-available/terraform.tf
new file mode 100644
index 0000000..fef3a32
--- /dev/null
+++ b/nginx-site-available/terraform.tf
@@ -0,0 +1,22 @@
+terraform {
+ required_providers {
+ docker = {
+ source = "kreuzwerker/docker"
+ version = "~>3.0"
+ }
+ random = {
+ source = "hashicorp/random"
+ version = "~>3.3"
+ }
+ local = {
+ source = "hashicorp/local"
+ version = "~>2.1"
+ }
+ scratch = {
+ source = "BrendanThompson/scratch"
+ version = "0.4.0"
+ }
+ }
+}
+
+
diff --git a/nginx.tf b/nginx.tf
new file mode 100644
index 0000000..08b4cdc
--- /dev/null
+++ b/nginx.tf
@@ -0,0 +1,78 @@
+data "docker_registry_image" "nginx" {
+ name = "nginx:latest"
+}
+resource "random_id" "iteration" {
+ keepers = {
+ configs = jsonencode(var.configs)
+ }
+ byte_length = 4
+}
+resource "docker_service" "nginx" {
+ name = var.service_name
+ mode {
+ replicated {
+ replicas = var.replicas
+ }
+ }
+ task_spec {
+ container_spec {
+ image = "${data.docker_registry_image.nginx.name}@${data.docker_registry_image.nginx.sha256_digest}"
+ configs {
+ config_id = docker_config.default_page.id
+ config_name = docker_config.default_page.name
+ file_name = "/usr/share/nginx/html/index.html"
+ }
+ configs {
+ config_id = docker_config.default_conf.id
+ config_name = docker_config.default_conf.name
+ file_name = "/etc/nginx/conf.d/default.conf"
+ }
+ dynamic "configs" {
+ for_each = var.configs
+ content {
+ config_id = configs.value.id
+ config_name = configs.value.name
+ file_name = "/etc/nginx/conf.d/${configs.value.file}"
+ }
+ }
+ # Healthcheck that checks that the nginx process is running
+ #healthcheck {
+ # test = ["CMD", "pgrep", "nginx"]
+ # interval = "10s"
+ # timeout = "5s"
+ # retries = 3
+ # start_period = "10s"
+ #}
+ healthcheck {
+ test = ["CMD", "true"]
+ }
+ labels {
+ label = "com.nginx.iteration-id"
+ value = random_id.iteration.hex
+ }
+ }
+ dynamic "networks_advanced" {
+ for_each = var.networks
+ content {
+ name = networks_advanced.value.id
+ }
+ }
+ }
+ endpoint_spec {
+ ports {
+ target_port = 80
+ publish_mode = "ingress"
+ published_port = 80
+ }
+ ports {
+ target_port = 443
+ publish_mode = "ingress"
+ published_port = 443
+ }
+ }
+ update_config {
+ parallelism = ceil(var.replicas / 3)
+ delay = "10s"
+ order = "start-first"
+ }
+}
diff --git a/terraform.tf b/terraform.tf
new file mode 100644
index 0000000..4b30c7e
--- /dev/null
+++ b/terraform.tf
@@ -0,0 +1,14 @@
+terraform {
+ required_providers {
+ docker = {
+ source = "kreuzwerker/docker"
+ version = "~>3.0"
+ }
+ scratch = {
+ source = "BrendanThompson/scratch"
+ version = "0.4.0"
+ }
+ }
+}
+
+