diff --git a/example/.tflint.hcl b/example/.tflint.hcl new file mode 100644 index 0000000..e927895 --- /dev/null +++ b/example/.tflint.hcl @@ -0,0 +1,16 @@ +plugin "terraform" { + enabled = true + preset = "recommended" +} + +rule "terraform_unused_declarations" { + enabled = true +} + +# rule "terraform_documented_outputs" { +# enabled = true +# } + +rule "terraform_documented_variables" { + enabled = true +} diff --git a/example/main.tf b/example/main.tf new file mode 100644 index 0000000..1f66eb9 --- /dev/null +++ b/example/main.tf @@ -0,0 +1,130 @@ +resource "stackit_network_area" "project_sna" { + organization_id = var.organization_id + name = var.SNA_name + network_ranges = var.SNA_network_ranges + transfer_network = var.SNA_transfer_network +} + +module "projects" { + source = "../project" + + projects = var.Projects_map + organization_id = var.organization_id + sna_id = stackit_network_area.project_sna.network_area_id + labels = var.labels != null ? var.labels : {} +} + +locals { + project_ids = { for k, v in module.projects.created_projects : k => v.project_id } +} + +module "stackit_ske_cluster" { + source = "../ske" + for_each = var.ske_clusters + + project_id = local.project_ids[each.value.project_key] + name = each.value.name + node_pools = each.value.node_pools + kubernetes_version_min = lookup(each.value, "kubernetes_version_min", null) + hibernations = lookup(each.value, "hibernations", null) + maintenance = lookup(each.value, "maintenance", null) + extensions = lookup(each.value, "extensions", null) + default_region = var.default_region +} + +module "security_groups" { + source = "../security-group" + + for_each = var.security_groups + + project_id = local.project_ids[each.value.project_key] + name = each.value.name + description = each.value.description != null ? each.value.description : "" + rules = each.value.rules +} + +locals { + security_group_ids_by_name = { + for key, m in module.security_groups : + m.name => m.security_group_id + } +} + +module "net" { + depends_on = [module.security_groups] + source = "../network" + + for_each = var.networks + + project_id = local.project_ids[each.value.project_key] + name = each.value.name + + ipv4_gateway = each.value.ipv4_gateway + ipv4_nameservers = each.value.ipv4_nameservers + ipv4_prefix = each.value.ipv4_prefix + ipv4_prefix_length = each.value.ipv4_prefix_length + + ipv6_gateway = each.value.ipv6_gateway + ipv6_nameservers = each.value.ipv6_nameservers + ipv6_prefix = each.value.ipv6_prefix + ipv6_prefix_length = each.value.ipv6_prefix_length + + no_ipv4_gateway = each.value.no_ipv4_gateway + no_ipv6_gateway = each.value.no_ipv6_gateway + routed = each.value.routed + labels = each.value.labels + + nics = each.value.nics + security_group_ids_by_name = local.security_group_ids_by_name +} + +module "postgres" { + source = "../postgres" + + for_each = var.postgres_instances + + project_id = local.project_ids[each.value.project_key] + name = each.value.name + ver = each.value.version + flavor = each.value.flavor + storage = each.value.storage + replicas = each.value.replicas + acl = each.value.acl + backup_schedule = each.value.backup_schedule + users = each.value.users + databases = each.value.databases +} + +module "observability" { + source = "../observability" + for_each = var.observability_instances + + project_id = local.project_ids[each.value.project_key] + + name = each.value.name + plan_name = each.value.plan_name + + acl = each.value.acl + metrics_retention_days = each.value.metrics_retention_days + metrics_retention_days_5m_downsampling = each.value.metrics_retention_days_5m_downsampling + metrics_retention_days_1h_downsampling = each.value.metrics_retention_days_1h_downsampling + alert_config = each.value.alert_config + parameters = each.value.parameters + + # Credentials + create_credentials = each.value.create_credentials + credentials_count = each.value.credentials_count + + alertgroups = each.value.alertgroups + + logalertgroups = each.value.logalertgroups + + scrapeconfigs = each.value.scrapeconfigs +} + +output "obs_url" { + value = { + for key, instance in module.observability : + key => instance.observability_urls + } +} diff --git a/example/providers.tf b/example/providers.tf new file mode 100644 index 0000000..5873620 --- /dev/null +++ b/example/providers.tf @@ -0,0 +1,16 @@ +terraform { + required_version = ">= 1.9.0" + required_providers { + stackit = { + source = "stackitcloud/stackit" + version = "0.56.0" + } + } +} + +provider "stackit" { + default_region = var.region + service_account_token = var.service_account_token != null ? var.service_account_token : null + service_account_key_path = var.service_account_key_path != null ? var.service_account_key_path : null + enable_beta_resources = true +} diff --git a/example/terraform.tfvars b/example/terraform.tfvars new file mode 100644 index 0000000..9b09f84 --- /dev/null +++ b/example/terraform.tfvars @@ -0,0 +1,295 @@ +organization_id = "03a34540-3c1a-4794-b2c6-7111ecf824ef" + +Projects_map = { + "projekt-alpha" = { + name = "tf_modules_test_6_jh" + owner_email = "maximilian.schlenz@stackit.cloud" + }, + "projekt-beta" = { + name = "tf_modules_test_7_jh" + owner_email = "maximilian.schlenz@stackit.cloud" + } +} + +SNA_name = "sna-jh" + +SNA_network_ranges = [ + { prefix = "192.168.10.0/24" } +] + +SNA_transfer_network = "172.16.0.0/24" + +security_groups = { + ssh_ingress_group = { + name = "ssh-ingress-group" + project_key = "projekt-alpha" + description = "ALLOW SSH ingress" + rules = [ + { description = "SSH RULE 1" + direction = "ingress" + ether_type = "IPv4" + ip_range = "0.0.0.0/0" + protocol = { + name = "tcp" + } + port_range = { + min = 22 + max = 22 + } + }, + ] + }, + + web_traffic_group = { + name = "web-traffic-group" + project_key = "projekt-alpha" + description = "ALLOW WEB TRAFFIC ingress" + rules = [ + { description = "ALLOW ALL 80" + direction = "ingress" + ether_type = "IPv4" + ip_range = "0.0.0.0/0" + protocol = { + name = "tcp" + } + port_range = { + min = 80 + max = 80 + } + }, + { description = "ALLOW ALL 443" + direction = "ingress" + ether_type = "IPv4" + ip_range = "0.0.0.0/0" + protocol = { + name = "tcp" + } + port_range = { + min = 443 + max = 443 + } + }, + ] + }, +} + +postgres_instances = { + dev = { + name = "pg-test-instance" + project_key = "projekt-alpha" + version = 17 + flavor = { + cpu = 2, + ram = 4 + } + storage = { + class = "premium-perf6-stackit", + size = 20 + } + replicas = 1 + acl = ["0.0.0.0/0"] + backup_schedule = "00 00 * * *" + + users = [ + { username = "adminusr", + roles = ["login", "createdb"] + }, + { username = "testusr", + roles = ["login"] + } + ] + + databases = [ + { + name = "testdb", + owner = "admin" + } + ] + } +} + +networks = { + wan_network = { + name = "wan_network" + project_key = "projekt-beta" + ipv4_nameservers = ["1.1.1.1", "8.8.8.8"] + ipv4_prefix_length = 29 + ipv4_prefix = "192.168.10.248/29" + routed = true + } +} + +observability_instances = { + test = { + # Required + name = "test-observability" + project_key = "projekt-alpha" + plan_name = "Observability-Large-EU01" + + # Optional instance settings + acl = ["192.168.100.10/32", "203.0.113.5/32"] + metrics_retention_days = 30 + metrics_retention_days_5m_downsampling = 10 + metrics_retention_days_1h_downsampling = 5 + # parameters = { + # "custom_param" = "value" + # } + + # Credentials + create_credentials = true + credentials_count = 2 + + # alert‑groups + alertgroups = { + test_group = { + name = "example-alert-group" + interval = "60s" + rules = [ + { + alert = "example-alert-name" + expression = "kube_node_status_condition{condition=\"Ready\", status=\"false\"} > 0" + for = "60s" + labels = { + severity = "critical" + } + annotations = { + summary = "example summary" + description = "example description" + } + }, + { + alert = "example-alert-name-2" + expression = "kube_node_status_condition{condition=\"Ready\", status=\"false\"} > 0" + for = "1m" + labels = { + severity = "critical" + } + annotations = { + summary = "example summary" + description = "example description" + } + }, + ] + } + } + + # log‑alert‑groups + logalertgroups = { + example_log = { + name = "example-log-alert-group" + interval = "60m" + rules = [ + { + alert = "example-log-alert-name" + expression = "sum(rate({namespace=\"example\", pod=\"logger\"} |= \"Simulated error message\" [1m])) > 0" + for = "60s" + labels = { + severity = "critical" + } + annotations = { + summary = "example summary" + description = "example description" + } + }, + { + alert = "example-log-alert-name-2" + expression = "sum(rate({namespace=\"example\", pod=\"logger\"} |= \"Another error message\" [1m])) > 0" + for = "60s" + labels = { + severity = "critical" + } + annotations = { + summary = "example summary" + description = "example description" + } + }, + ] + } + } + + # scrapeconfigs + scrapeconfigs = { + example_job = { + name = "example-job" + metrics_path = "/my-metrics" + saml2 = { + enable_url_parameters = true + } + targets = [ + { + urls = ["url1", "urls2"] + labels = { + "url1" = "dev" + } + } + ] + } + } + } +} + +ske_clusters = { + /* + "prod-cluster" = { + name = "clusterprod" + project_key = "projekt-alpha" + kubernetes_version_min = "1.32.5" + + node_pools = [ + { + name = "np" + machine_type = "c1.2" + availability_zones = ["eu01-3", "eu01-1"] + minimum = 2 + maximum = 3 + volume_type = "storage_premium_perf1" + volume_size = 64 + taints = [{ + effect = "NoSchedule" + key = "app" + value = "database" + }] + } + ] + + hibernations = [ + { + start = "00 18 * * 1-5" # Mo-Fr um 18:00 Uhr + end = "00 08 * * 1-5" # Mo-Fr um 08:00 Uhr + timezone = "Europe/Berlin" + } + ] + + maintenance = { + enable_kubernetes_version_updates = true + enable_machine_image_version_updates = true + start = "01:00:00Z" + end = "03:00:00Z" + } + + extensions = { + acl = { + enabled = true + allowed_cidrs = ["0.0.0.0/0"] + } + } + }, + */ + "dev-cluster" = { + name = "cluster" + kubernetes_version_min = "1.32.5" + project_key = "projekt-alpha" + + node_pools = [ + { + name = "np" + machine_type = "g1.4" + availability_zones = ["eu01-2"] + minimum = 1 + maximum = 2 + volume_size = 21 + } + ] + } + +} diff --git a/example/variables.tf b/example/variables.tf new file mode 100644 index 0000000..dff5a62 --- /dev/null +++ b/example/variables.tf @@ -0,0 +1,313 @@ +variable "region" { + description = "Provider region for STACKIT Cloud" + type = string + default = "eu01" +} + +variable "service_account_token" { + description = "Service account token for authentication" + sensitive = true + type = string + default = null +} + +# -----------------------------------------------------------------------------# +# SNA & Projects +# -----------------------------------------------------------------------------# + +variable "organization_id" { + description = "STACKIT organization container ID" + type = string +} + +variable "service_account_key_path" { + description = "Path to service account JSON key" + type = string + default = "/Users/schlenz/.stackit/sa.json" +} + +variable "default_region" { + description = "Default region fallback for created resources" + type = string + default = "eu01" +} + +variable "SNA_name" { + description = "Name of the Service Network Area to create" + type = string +} + +variable "SNA_network_ranges" { + description = "CIDR list for the Service Network Area" + type = list(object({ prefix = string })) +} + +variable "SNA_transfer_network" { + description = "Transfer network CIDR for the SNA" + type = string +} + +variable "Projects_map" { + description = "Map of STACKIT projects to create" + type = map(object({ + name = string + owner_email = string + })) +} + +variable "labels" { + description = "Default labels to apply where supported" + type = map(string) + default = {} +} + +variable "security_groups" { + description = "Map of security group definitions" + type = map(object({ + name = optional(string) + project_key = string + description = optional(string) + rules = list(object({ + direction = string + description = optional(string) + ether_type = optional(string) + icmp_parameters = optional(object({ + type = optional(number) + code = optional(number) + })) + ip_range = optional(string) + port_range = optional(object({ + min = number + max = number + })) + protocol = optional(object({ + name = optional(string) + number = optional(number) + })) + remote_security_group_id = optional(string) + })) + })) +} + +variable "postgres_instances" { + description = "Map of PostgreSQL instances to create" + type = map(object({ + name = string + project_key = string + version = number + flavor = object({ cpu = number, ram = number }) + storage = object({ class = string, size = number }) + replicas = number + acl = list(string) + backup_schedule = string + users = list(object({ + username = string + roles = set(string) + })) + databases = list(object({ + name = string + owner = string + })) + })) +} + +variable "networks" { + description = "Map of network definitions per project" + type = map(object({ + name = string + project_key = string + + # IPv4 + ipv4_gateway = optional(string) + ipv4_nameservers = optional(list(string)) + ipv4_prefix = optional(string) + ipv4_prefix_length = optional(number) + + # IPv6 + ipv6_gateway = optional(string) + ipv6_nameservers = optional(list(string)) + ipv6_prefix = optional(string) + ipv6_prefix_length = optional(number) + + # Flags & labels + labels = optional(map(string)) + no_ipv4_gateway = optional(bool) + no_ipv6_gateway = optional(bool) + routed = optional(bool) + + # NICs + nics = optional(map(object({ + nic_ipv4 = optional(string) + nic_name = string + nic_allowed_addresses = optional(list(string)) + nic_labels = optional(map(string)) + nic_security = optional(bool) + nic_security_group_ids = optional(list(string)) + nic_security_group_names = optional(list(string)) + }))) + })) + default = {} +} + +# -----------------------------------------------------------------------------# +# SKE Clusters +# -----------------------------------------------------------------------------# + +variable "ske_clusters" { + description = "Map of SKE cluster definitions" + type = map(object({ + name = string + project_key = string + kubernetes_version_min = optional(string) + hibernations = optional(list(object({ + start = string + end = string + timezone = optional(string) + }))) + maintenance = optional(object({ + enable_kubernetes_version_updates = bool + enable_machine_image_version_updates = bool + start = string + end = string + })) + extensions = optional(object({ + acl = optional(object({ + enabled = bool + allowed_cidrs = list(string) + })) + argus = optional(object({ + enabled = bool + argus_instance_id = string + })) + })) + + node_pools = list(object({ + name = string + machine_type = string + availability_zones = list(string) + minimum = number + maximum = number + + allow_system_components = optional(bool) + cri = optional(string) + labels = optional(map(string)) + max_surge = optional(number) + max_unavailable = optional(number) + os_name = optional(string) + os_version_min = optional(string) + volume_size = optional(number) + volume_type = optional(string) + taints = optional(list(object({ + effect = string + key = string + value = optional(string) + }))) + })) + })) + default = {} +} + +variable "observability_instances" { + description = "Map of Observability instances to create" + type = map(object({ + name = string + project_key = string + plan_name = string + + # Instance settings + acl = optional(list(string)) + metrics_retention_days = optional(number) + metrics_retention_days_5m_downsampling = optional(number) + metrics_retention_days_1h_downsampling = optional(number) + alert_config = optional(any) + parameters = optional(map(string)) + + # Credentials + create_credentials = optional(bool, true) + credentials_count = optional(number, 1) + + # Alert-groups + alertgroups = optional(map(object({ + name = string + interval = optional(string) + rules = list(object({ + alert = string + expression = string + for = optional(string) + labels = optional(map(string)) + annotations = optional(map(string)) + })) + })), {}) + + # Log-alert-groups + logalertgroups = optional(map(object({ + name = string + interval = optional(string) + rules = list(object({ + alert = string + expression = string + for = optional(string) + labels = optional(map(string)) + annotations = optional(map(string)) + })) + })), {}) + + # Scrape-configs + scrapeconfigs = optional(map(object({ + name = string + metrics_path = string + targets = list(object({ + urls = list(string) + labels = optional(map(string)) + })) + basic_auth = optional(object({ + username = string + password = string + })) + saml2 = optional(object({ + enable_url_parameters = optional(bool) + })) + sample_limit = optional(number) + scheme = optional(string) + scrape_interval = optional(string) + scrape_timeout = optional(string) + })), {}) + })) + default = {} + + validation { + condition = alltrue([ + for k, v in var.observability_instances : + contains([ + "Observability-Medium-EU01", + "Observability-Monitoring-XL-EU01", + "Observability-Large-EU01", + "Observability-Monitoring-Basic-EU01", + "Observability-Monitoring-Large-EU01", + "Observability-Basic-EU01", + "Observability-Monitoring-Medium-EU01", + "Observability-Monitoring-XXL-EU01", + "Observability-Metrics-Endpoint-100k-EU01", + "Observability-Frontend-Starter-EU01", + "Observability-Monitoring-Starter-EU01", + "Observability-Starter-EU01", + ], v.plan_name) + ]) + error_message = <<-EOM + One or more observability_instances specify an invalid plan_name. + See the provider error output for supported plans. Allowed: + Observability-Medium-EU01 + Observability-Monitoring-XL-EU01 + Observability-Large-EU01 + Observability-Monitoring-Basic-EU01 + Observability-Monitoring-Large-EU01 + Observability-Basic-EU01 + Observability-Monitoring-Medium-EU01 + Observability-Monitoring-XXL-EU01 + Observability-Metrics-Endpoint-100k-EU01 + Observability-Frontend-Starter-EU01 + Observability-Monitoring-Starter-EU01 + Observability-Starter-EU01 + EOM + } +} diff --git a/project_old/main.tf b/project_old/main.tf new file mode 100644 index 0000000..58176f0 --- /dev/null +++ b/project_old/main.tf @@ -0,0 +1,34 @@ +variable "projects" { + type = map(object({ + name = string + owner_email = string + })) + default = { + project1 = { + name = "project-alpha" + owner_email = "michael.sodan@stackit.cloud" + } + project2 = { + name = "project-beta" + owner_email = "michael.sodan@stackit.cloud" + } + } +} + +resource "stackit_resourcemanager_project" "projects" { + for_each = var.projects + parent_container_id = var.organization_id # Nutzt jetzt die übergebene Variable + name = each.value.name + owner_email = each.value.owner_email + # labels = { ... } # Vorerst entfernt, da stackit_network_area nicht definiert war +} + +output "project_info" { + value = { + for k, project in stackit_resourcemanager_project.projects : k => { + project_id = project.project_id + container_id = project.container_id + } + } +} + diff --git a/project_old/provider.tf b/project_old/provider.tf new file mode 100644 index 0000000..a89dd02 --- /dev/null +++ b/project_old/provider.tf @@ -0,0 +1,10 @@ + +terraform { + required_providers { + stackit = { + source = "stackitcloud/stackit" + version = ">= 0.54.0" # Diese Version passt zu Ihrer Anforderung + } + } +} + diff --git a/project_old/sna.tf b/project_old/sna.tf new file mode 100644 index 0000000..711c80d --- /dev/null +++ b/project_old/sna.tf @@ -0,0 +1,27 @@ +/* resource "time_sleep" "wait_before_destroy" { + destroy_duration = "60s" +} +*/ + +resource "stackit_network_area" "sna" { + organization_id = var.organization_id + name = "bego_sna" + network_ranges = [ + { + prefix = "10.220.0.0/16" + } + ] + transfer_network = "var.sna_net" + //depends_on = [time_sleep.wait_before_destroy] +} + +/* resource "stackit_network_area_route" "sna_route1" { + organization_id = var.organization_id + network_area_id = stackit_network_area.sna.network_area_id + prefix = "10.220.99.0/24" + next_hop = "10.220.0.0" + labels = { + "key" = "value" + } +} +*/ diff --git a/project_old/variables.tf b/project_old/variables.tf new file mode 100644 index 0000000..6a8400f --- /dev/null +++ b/project_old/variables.tf @@ -0,0 +1,10 @@ +variable "organization_id" { + description = "Empfängt die Container-ID der Organisation vom Root-Modul." + type = string +} + +variable "sna_net" { + description = "SNA Transfer Network" + type = string +} +