From 3a41f0d302c68cbe9e180a7b0d4947255af365fa Mon Sep 17 00:00:00 2001 From: Maximilian Schlenz Date: Fri, 4 Jul 2025 14:57:27 +0200 Subject: [PATCH 1/4] add module for security-group and security-group-rule --- security-group-rule/main.tf | 19 ++++++++++++++++ security-group-rule/output.tf | 3 +++ security-group-rule/providers.tf | 9 ++++++++ security-group-rule/variables.tf | 39 ++++++++++++++++++++++++++++++++ security-group/main.tf | 5 ++++ security-group/output.tf | 7 ++++++ security-group/providers.tf | 9 ++++++++ security-group/variables.tf | 12 ++++++++++ 8 files changed, 103 insertions(+) create mode 100644 security-group-rule/main.tf create mode 100644 security-group-rule/output.tf create mode 100644 security-group-rule/providers.tf create mode 100644 security-group-rule/variables.tf create mode 100644 security-group/main.tf create mode 100644 security-group/output.tf create mode 100644 security-group/providers.tf create mode 100644 security-group/variables.tf diff --git a/security-group-rule/main.tf b/security-group-rule/main.tf new file mode 100644 index 0000000..b7abf70 --- /dev/null +++ b/security-group-rule/main.tf @@ -0,0 +1,19 @@ +locals { + rule_count = length(var.rules) +} + +resource "stackit_security_group_rule" "this" { + count = local.rule_count + + project_id = var.project_id + security_group_id = var.security_group_id + + direction = var.rules[count.index].direction + description = var.rules[count.index].description + ether_type = var.rules[count.index].ether_type + icmp_parameters = var.rules[count.index].icmp_parameters + ip_range = var.rules[count.index].ip_range + port_range = var.rules[count.index].port_range + protocol = var.rules[count.index].protocol + remote_security_group_id = var.rules[count.index].remote_security_group_id +} diff --git a/security-group-rule/output.tf b/security-group-rule/output.tf new file mode 100644 index 0000000..d36affc --- /dev/null +++ b/security-group-rule/output.tf @@ -0,0 +1,3 @@ +output "rule_ids" { + value = stackit_security_group_rule.this[*].id +} diff --git a/security-group-rule/providers.tf b/security-group-rule/providers.tf new file mode 100644 index 0000000..6e038c3 --- /dev/null +++ b/security-group-rule/providers.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.9.0" + required_providers { + stackit = { + source = "stackitcloud/stackit" + version = "0.56.0" + } + } +} diff --git a/security-group-rule/variables.tf b/security-group-rule/variables.tf new file mode 100644 index 0000000..04221b5 --- /dev/null +++ b/security-group-rule/variables.tf @@ -0,0 +1,39 @@ +variable "project_id" { + type = string +} + +variable "security_group_id" { + type = string +} + +variable "rules" { + type = 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) + })) + default = [] + + validation { + condition = alltrue([ + for rule in var.rules : contains(["ingress", "egress"], rule.direction) + # ... need more validations + ]) + error_message = "Direction must be either \"ingress\" or \"egress\"." + } +} + diff --git a/security-group/main.tf b/security-group/main.tf new file mode 100644 index 0000000..a0e124c --- /dev/null +++ b/security-group/main.tf @@ -0,0 +1,5 @@ +resource "stackit_security_group" "this" { + project_id = var.project_id + name = var.name + description = var.description == null ? var.name : var.description +} diff --git a/security-group/output.tf b/security-group/output.tf new file mode 100644 index 0000000..b2d7334 --- /dev/null +++ b/security-group/output.tf @@ -0,0 +1,7 @@ +output "id" { + value = stackit_security_group.this.security_group_id +} + +output "name" { + value = stackit_security_group.this.name +} \ No newline at end of file diff --git a/security-group/providers.tf b/security-group/providers.tf new file mode 100644 index 0000000..6e038c3 --- /dev/null +++ b/security-group/providers.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.9.0" + required_providers { + stackit = { + source = "stackitcloud/stackit" + version = "0.56.0" + } + } +} diff --git a/security-group/variables.tf b/security-group/variables.tf new file mode 100644 index 0000000..9d74987 --- /dev/null +++ b/security-group/variables.tf @@ -0,0 +1,12 @@ +variable "project_id" { + type = string +} + +variable "name" { + type = string +} + +variable "description" { + type = string + default = null +} \ No newline at end of file From 45df23a7504ded3c4701fe94496b356343a12e8f Mon Sep 17 00:00:00 2001 From: Maximilian Schlenz Date: Fri, 4 Jul 2025 14:59:00 +0200 Subject: [PATCH 2/4] example stage to test modules --- example/main.tf | 32 +++++++++++++++++++++++ example/providers.tf | 15 +++++++++++ example/terraform.tfvars | 56 ++++++++++++++++++++++++++++++++++++++++ example/variables.tf | 28 ++++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 example/main.tf create mode 100644 example/providers.tf create mode 100644 example/terraform.tfvars create mode 100644 example/variables.tf diff --git a/example/main.tf b/example/main.tf new file mode 100644 index 0000000..e77a10e --- /dev/null +++ b/example/main.tf @@ -0,0 +1,32 @@ +module "icmp_group" { + source = "../security-group" + project_id = var.project_id + name = "icmp" +} + +module "ssh_group" { + source = "../security-group" + project_id = var.project_id + name = "ssh" +} + +module "icmp_ingress" { + source = "../security-group-rule" + project_id = var.project_id + security_group_id = module.icmp_group.id + rules = var.icmp_ingress_rules +} + +module "icmp_egress" { + source = "../security-group-rule" + project_id = var.project_id + security_group_id = module.icmp_group.id + rules = var.icmp_egress_rules +} + +module "ssh_ingress" { + source = "../security-group-rule" + project_id = var.project_id + security_group_id = module.ssh_group.id + rules = var.ssh_ingress_rules +} diff --git a/example/providers.tf b/example/providers.tf new file mode 100644 index 0000000..e5cfc8d --- /dev/null +++ b/example/providers.tf @@ -0,0 +1,15 @@ +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 + enable_beta_resources = true +} \ No newline at end of file diff --git a/example/terraform.tfvars b/example/terraform.tfvars new file mode 100644 index 0000000..2ae77cc --- /dev/null +++ b/example/terraform.tfvars @@ -0,0 +1,56 @@ +region = "eu01" +service_account_token = "" +project_id = "" + +# icmp_ingress_rules = [ +# { +# direction = "ingress" +# description = "ICMP RULE 1" +# ip_range = "0.0.0.0/0" +# protocol = { +# name = "icmp" +# } +# icmp_parameters = { +# type = 8, +# code = 0 +# } +# }, +# { +# direction = "ingress" +# description = "ICMP RULE 2" +# ip_range = "1.2.3.4/0" +# protocol = { +# name = "icmp" +# } +# icmp_parameters = { +# type = 8, +# code = 0 +# } +# } +# ] + +# ssh_ingress_rules = [ +# { +# direction = "ingress" +# description = "SSH RULE 1" +# ip_range = "10.1.10.1/24" +# port_range = { +# min = 22, +# max = 22 +# } +# protocol = { +# name = "tcp" +# } +# } +# ] + +# icmp_egress_rules = [ +# { +# direction = "egress" +# description = "ICMP EGRESS RULE 1" +# ip_range = "0.0.0.0/0" +# protocol = { +# name = "icmp" +# } +# } +# ] diff --git a/example/variables.tf b/example/variables.tf new file mode 100644 index 0000000..6b2ee76 --- /dev/null +++ b/example/variables.tf @@ -0,0 +1,28 @@ +variable "region" { + description = "Region for the STACKIT Cloud" + type = string + default = "eu01" +} + +variable "project_id" { + description = "STACKIT Cloud project ID" + type = string +} + +variable "service_account_token" { + description = "Service account token for authentication" + sensitive = true + type = string +} + +variable "icmp_ingress_rules" { + type = any +} + +variable "icmp_egress_rules" { + type = any +} + +variable "ssh_ingress_rules" { + type = any +} From 7b16b3e7d5b13f006266855e076f95cd0069a64d Mon Sep 17 00:00:00 2001 From: Maximilian Schlenz Date: Mon, 7 Jul 2025 09:16:42 +0200 Subject: [PATCH 3/4] refactor: consolidate security-group and security-group-rule --- .gitignore | 1 + example/main.tf | 38 +++--------- example/terraform.tfvars | 103 ++++++++++++++++--------------- example/variables.tf | 33 +++++++--- security-group-rule/main.tf | 19 ------ security-group-rule/output.tf | 3 - security-group-rule/providers.tf | 9 --- security-group-rule/variables.tf | 39 ------------ security-group/main.tf | 22 ++++++- security-group/output.tf | 9 +-- security-group/variables.tf | 43 +++++++++++-- 11 files changed, 150 insertions(+), 169 deletions(-) delete mode 100644 security-group-rule/main.tf delete mode 100644 security-group-rule/output.tf delete mode 100644 security-group-rule/providers.tf delete mode 100644 security-group-rule/variables.tf diff --git a/.gitignore b/.gitignore index 5dfe310..65bfb30 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .terraform* terraform.tfstate* +.env \ No newline at end of file diff --git a/example/main.tf b/example/main.tf index e77a10e..2d2fc0b 100644 --- a/example/main.tf +++ b/example/main.tf @@ -1,32 +1,10 @@ -module "icmp_group" { - source = "../security-group" - project_id = var.project_id - name = "icmp" -} +module "security_groups" { + source = "../security-group" -module "ssh_group" { - source = "../security-group" - project_id = var.project_id - name = "ssh" -} + for_each = var.security_groups -module "icmp_ingress" { - source = "../security-group-rule" - project_id = var.project_id - security_group_id = module.icmp_group.id - rules = var.icmp_ingress_rules -} - -module "icmp_egress" { - source = "../security-group-rule" - project_id = var.project_id - security_group_id = module.icmp_group.id - rules = var.icmp_egress_rules -} - -module "ssh_ingress" { - source = "../security-group-rule" - project_id = var.project_id - security_group_id = module.ssh_group.id - rules = var.ssh_ingress_rules -} + project_id = var.project_id + name = each.value.name + description = each.value.description + rules = each.value.rules +} \ No newline at end of file diff --git a/example/terraform.tfvars b/example/terraform.tfvars index 2ae77cc..584af52 100644 --- a/example/terraform.tfvars +++ b/example/terraform.tfvars @@ -2,55 +2,58 @@ region = "eu01" service_account_token = "" project_id = "" -# icmp_ingress_rules = [ -# { -# direction = "ingress" -# description = "ICMP RULE 1" -# ip_range = "0.0.0.0/0" -# protocol = { -# name = "icmp" -# } -# icmp_parameters = { -# type = 8, -# code = 0 -# } -# }, -# { -# direction = "ingress" -# description = "ICMP RULE 2" -# ip_range = "1.2.3.4/0" -# protocol = { -# name = "icmp" -# } -# icmp_parameters = { -# type = 8, -# code = 0 -# } -# } -# ] +security_groups = { + ssh_ingress_group = { + name = "ssh-ingress-group" + 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 + } + }, + ] + }, -# ssh_ingress_rules = [ -# { -# direction = "ingress" -# description = "SSH RULE 1" -# ip_range = "10.1.10.1/24" -# port_range = { -# min = 22, -# max = 22 -# } -# protocol = { -# name = "tcp" -# } -# } -# ] + web_traffic_group = { + name = "web-traffic-group" + 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 + } + }, + ] + }, -# icmp_egress_rules = [ -# { -# direction = "egress" -# description = "ICMP EGRESS RULE 1" -# ip_range = "0.0.0.0/0" -# protocol = { -# name = "icmp" -# } -# } -# ] +} \ No newline at end of file diff --git a/example/variables.tf b/example/variables.tf index 6b2ee76..918e44e 100644 --- a/example/variables.tf +++ b/example/variables.tf @@ -15,14 +15,29 @@ variable "service_account_token" { type = string } -variable "icmp_ingress_rules" { - type = any -} -variable "icmp_egress_rules" { - type = any -} - -variable "ssh_ingress_rules" { - type = any +variable "security_groups" { + type = map(object({ + name = optional(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) + })) + })) } diff --git a/security-group-rule/main.tf b/security-group-rule/main.tf deleted file mode 100644 index b7abf70..0000000 --- a/security-group-rule/main.tf +++ /dev/null @@ -1,19 +0,0 @@ -locals { - rule_count = length(var.rules) -} - -resource "stackit_security_group_rule" "this" { - count = local.rule_count - - project_id = var.project_id - security_group_id = var.security_group_id - - direction = var.rules[count.index].direction - description = var.rules[count.index].description - ether_type = var.rules[count.index].ether_type - icmp_parameters = var.rules[count.index].icmp_parameters - ip_range = var.rules[count.index].ip_range - port_range = var.rules[count.index].port_range - protocol = var.rules[count.index].protocol - remote_security_group_id = var.rules[count.index].remote_security_group_id -} diff --git a/security-group-rule/output.tf b/security-group-rule/output.tf deleted file mode 100644 index d36affc..0000000 --- a/security-group-rule/output.tf +++ /dev/null @@ -1,3 +0,0 @@ -output "rule_ids" { - value = stackit_security_group_rule.this[*].id -} diff --git a/security-group-rule/providers.tf b/security-group-rule/providers.tf deleted file mode 100644 index 6e038c3..0000000 --- a/security-group-rule/providers.tf +++ /dev/null @@ -1,9 +0,0 @@ -terraform { - required_version = ">= 1.9.0" - required_providers { - stackit = { - source = "stackitcloud/stackit" - version = "0.56.0" - } - } -} diff --git a/security-group-rule/variables.tf b/security-group-rule/variables.tf deleted file mode 100644 index 04221b5..0000000 --- a/security-group-rule/variables.tf +++ /dev/null @@ -1,39 +0,0 @@ -variable "project_id" { - type = string -} - -variable "security_group_id" { - type = string -} - -variable "rules" { - type = 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) - })) - default = [] - - validation { - condition = alltrue([ - for rule in var.rules : contains(["ingress", "egress"], rule.direction) - # ... need more validations - ]) - error_message = "Direction must be either \"ingress\" or \"egress\"." - } -} - diff --git a/security-group/main.tf b/security-group/main.tf index a0e124c..17a4dba 100644 --- a/security-group/main.tf +++ b/security-group/main.tf @@ -1,5 +1,25 @@ +locals { + rule_count = length(var.rules) +} + resource "stackit_security_group" "this" { project_id = var.project_id name = var.name - description = var.description == null ? var.name : var.description + description = var.description +} + +resource "stackit_security_group_rule" "rule" { + count = local.rule_count + + direction = var.rules[count.index].direction + project_id = var.project_id + security_group_id = stackit_security_group.this.id + + description = var.rules[count.index].description + ether_type = var.rules[count.index].ether_type + icmp_parameters = var.rules[count.index].icmp_parameters + ip_range = var.rules[count.index].ip_range + port_range = var.rules[count.index].port_range + protocol = var.rules[count.index].protocol + remote_security_group_id = var.rules[count.index].remote_security_group_id } diff --git a/security-group/output.tf b/security-group/output.tf index b2d7334..bf320d2 100644 --- a/security-group/output.tf +++ b/security-group/output.tf @@ -1,7 +1,8 @@ -output "id" { - value = stackit_security_group.this.security_group_id +output "security_group_id" { + value = stackit_security_group.this.security_group_id + description = "ID of the security group" } -output "name" { - value = stackit_security_group.this.name +output "rule_ids" { + value = stackit_security_group_rule.rule[*].id } \ No newline at end of file diff --git a/security-group/variables.tf b/security-group/variables.tf index 9d74987..4367b87 100644 --- a/security-group/variables.tf +++ b/security-group/variables.tf @@ -1,12 +1,45 @@ variable "project_id" { - type = string + type = string + description = "The ID of the project where the security group will be created." } variable "name" { - type = string + type = string + description = "Name of the security group." } variable "description" { - type = string - default = null -} \ No newline at end of file + type = string + default = "" + description = "Description of the security group. If not provided, it defaults to an empty string." +} + +variable "rules" { + description = "List of rules to attach to this security-group" + type = 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) + })) + remote_security_group_id = optional(string) + })) + default = [] + validation { + condition = alltrue([ + for rule in var.rules : contains(["ingress", "egress"], rule.direction) + # ... need more validations + ]) + error_message = "Direction must be either \"ingress\" or \"egress\"." + } +} From 93f2af55c729ffb38b8760e0617789f110c20b88 Mon Sep 17 00:00:00 2001 From: Maximilian Schlenz Date: Mon, 14 Jul 2025 13:55:16 +0200 Subject: [PATCH 4/4] update structure for nested resources in project --- security-group/main.tf | 2 +- security-group/variables.tf | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/security-group/main.tf b/security-group/main.tf index 17a4dba..3098cf0 100644 --- a/security-group/main.tf +++ b/security-group/main.tf @@ -13,7 +13,7 @@ resource "stackit_security_group_rule" "rule" { direction = var.rules[count.index].direction project_id = var.project_id - security_group_id = stackit_security_group.this.id + security_group_id = stackit_security_group.this.security_group_id description = var.rules[count.index].description ether_type = var.rules[count.index].ether_type diff --git a/security-group/variables.tf b/security-group/variables.tf index 4367b87..5ccff74 100644 --- a/security-group/variables.tf +++ b/security-group/variables.tf @@ -17,7 +17,7 @@ variable "description" { variable "rules" { description = "List of rules to attach to this security-group" type = list(object({ - direction = string + direction = string description = optional(string) ether_type = optional(string) icmp_parameters = optional(object({ @@ -30,12 +30,12 @@ variable "rules" { max = number })) protocol = optional(object({ - name = optional(string) + name = optional(string) })) remote_security_group_id = optional(string) })) default = [] - validation { + validation { condition = alltrue([ for rule in var.rules : contains(["ingress", "egress"], rule.direction) # ... need more validations