From 9daa08355828af337fa67f1b8570aacc40828b6f Mon Sep 17 00:00:00 2001 From: Mauritz Uphoff Date: Wed, 17 Sep 2025 11:04:02 +0200 Subject: [PATCH] Initial commit --- .gitignore | 8 +++ .terraform.lock.hcl | 104 +++++++++++++++++++++++++++++++ 000-backend.tf | 1 + 010-provider.tf | 53 ++++++++++++++++ 020-variables.tf | 14 +++++ 030-ske.tf | 35 +++++++++++ 040-secrets-manager.tf | 11 ++++ 050-random-secret.tf | 20 ++++++ 060-external-secrets-operator.tf | 16 +++++ 070-cluster-secret-store.tf | 41 ++++++++++++ 080-external-secret.tf | 37 +++++++++++ 11 files changed, 340 insertions(+) create mode 100644 .gitignore create mode 100644 .terraform.lock.hcl create mode 100644 000-backend.tf create mode 100644 010-provider.tf create mode 100644 020-variables.tf create mode 100644 030-ske.tf create mode 100644 040-secrets-manager.tf create mode 100644 050-random-secret.tf create mode 100644 060-external-secrets-operator.tf create mode 100644 070-cluster-secret-store.tf create mode 100644 080-external-secret.tf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9a45efd --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.terraform +terraform.tfstate +terraform.tfstate.backup +.idea +.terraform.tfstate.lock.info +keys/stackit-sa.json +keys/* +.keys/* \ No newline at end of file diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl new file mode 100644 index 0000000..4bf4346 --- /dev/null +++ b/.terraform.lock.hcl @@ -0,0 +1,104 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/helm" { + version = "2.17.0" + constraints = "2.17.0" + hashes = [ + "h1:kQMkcPVvHOguOqnxoEU2sm1ND9vCHiT8TvZ2x6v/Rsw=", + "zh:06fb4e9932f0afc1904d2279e6e99353c2ddac0d765305ce90519af410706bd4", + "zh:104eccfc781fc868da3c7fec4385ad14ed183eb985c96331a1a937ac79c2d1a7", + "zh:129345c82359837bb3f0070ce4891ec232697052f7d5ccf61d43d818912cf5f3", + "zh:3956187ec239f4045975b35e8c30741f701aa494c386aaa04ebabffe7749f81c", + "zh:66a9686d92a6b3ec43de3ca3fde60ef3d89fb76259ed3313ca4eb9bb8c13b7dd", + "zh:88644260090aa621e7e8083585c468c8dd5e09a3c01a432fb05da5c4623af940", + "zh:a248f650d174a883b32c5b94f9e725f4057e623b00f171936dcdcc840fad0b3e", + "zh:aa498c1f1ab93be5c8fbf6d48af51dc6ef0f10b2ea88d67bcb9f02d1d80d3930", + "zh:bf01e0f2ec2468c53596e027d376532a2d30feb72b0b5b810334d043109ae32f", + "zh:c46fa84cc8388e5ca87eb575a534ebcf68819c5a5724142998b487cb11246654", + "zh:d0c0f15ffc115c0965cbfe5c81f18c2e114113e7a1e6829f6bfd879ce5744fbb", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} + +provider "registry.terraform.io/hashicorp/kubernetes" { + version = "2.38.0" + constraints = ">= 2.25.2" + hashes = [ + "h1:soK8Lt0SZ6dB+HsypFRDzuX/npqlMU6M0fvyaR1yW0k=", + "zh:0af928d776eb269b192dc0ea0f8a3f0f5ec117224cd644bdacdc682300f84ba0", + "zh:1be998e67206f7cfc4ffe77c01a09ac91ce725de0abaec9030b22c0a832af44f", + "zh:326803fe5946023687d603f6f1bab24de7af3d426b01d20e51d4e6fbe4e7ec1b", + "zh:4a99ec8d91193af961de1abb1f824be73df07489301d62e6141a656b3ebfff12", + "zh:5136e51765d6a0b9e4dbcc3b38821e9736bd2136cf15e9aac11668f22db117d2", + "zh:63fab47349852d7802fb032e4f2b6a101ee1ce34b62557a9ad0f0f0f5b6ecfdc", + "zh:924fb0257e2d03e03e2bfe9c7b99aa73c195b1f19412ca09960001bee3c50d15", + "zh:b63a0be5e233f8f6727c56bed3b61eb9456ca7a8bb29539fba0837f1badf1396", + "zh:d39861aa21077f1bc899bc53e7233262e530ba8a3a2d737449b100daeb303e4d", + "zh:de0805e10ebe4c83ce3b728a67f6b0f9d18be32b25146aa89116634df5145ad4", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + "zh:faf23e45f0090eef8ba28a8aac7ec5d4fdf11a36c40a8d286304567d71c1e7db", + ] +} + +provider "registry.terraform.io/hashicorp/random" { + version = "3.7.2" + constraints = "3.7.2" + hashes = [ + "h1:KG4NuIBl1mRWU0KD/BGfCi1YN/j3F7H4YgeeM7iSdNs=", + "zh:14829603a32e4bc4d05062f059e545a91e27ff033756b48afbae6b3c835f508f", + "zh:1527fb07d9fea400d70e9e6eb4a2b918d5060d604749b6f1c361518e7da546dc", + "zh:1e86bcd7ebec85ba336b423ba1db046aeaa3c0e5f921039b3f1a6fc2f978feab", + "zh:24536dec8bde66753f4b4030b8f3ef43c196d69cccbea1c382d01b222478c7a3", + "zh:29f1786486759fad9b0ce4fdfbbfece9343ad47cd50119045075e05afe49d212", + "zh:4d701e978c2dd8604ba1ce962b047607701e65c078cb22e97171513e9e57491f", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:7b8434212eef0f8c83f5a90c6d76feaf850f6502b61b53c329e85b3b281cba34", + "zh:ac8a23c212258b7976e1621275e3af7099e7e4a3d4478cf8d5d2a27f3bc3e967", + "zh:b516ca74431f3df4c6cf90ddcdb4042c626e026317a33c53f0b445a3d93b720d", + "zh:dc76e4326aec2490c1600d6871a95e78f9050f9ce427c71707ea412a2f2f1a62", + "zh:eac7b63e86c749c7d48f527671c7aee5b4e26c10be6ad7232d6860167f99dbb0", + ] +} + +provider "registry.terraform.io/hashicorp/vault" { + version = "5.3.0" + hashes = [ + "h1:Am6aiwVOzLPYlv8QWhPfuin7cF3GuKdwimN72LAuAgE=", + "zh:23575c48c28afba789e8b8242f0cafb3f4893fddffca7c48fde97c4d7b1d5066", + "zh:2bccfac0b16675c380621ee5b96cf835cbf5e675a3113919964fbdf66c531db2", + "zh:3f4999e58f33287d3967e60b7a11d2605a09096b345c1f538d18aad8fdf74a3d", + "zh:64ec7b92dd9897d2ee862df3ba44ca16d67ddbe54d7307768327076f8af7ad31", + "zh:7695db56a6e5b7b3fb554714cf7c8c2178256bd6e77066a925beb26c104692b9", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:89f512a71af21ca2f273abf2d6360cd92e0dd090d313a903bcf76c120db22559", + "zh:badcc556c3a5528118c9ae69d3c67ba83fed0838cc6a3fc467d41ed15b20aa98", + "zh:c93e1e2589f80bd87fd34e19ae03441a7f73613d79c7ad536c37bc7d52151aee", + "zh:cd32b7b2823b1da16c4f530af94a2fc14b4212bbecc4c5ab4972eccca291281f", + "zh:e2988670b84a87a434d8dec48c9e2b77f7744cad6fe9a74a946200b3ab9b7ba3", + "zh:e9e37b61a3d9c3471e9486ecb944e12b091c3d53cabbc324b593363f77be046c", + ] +} + +provider "registry.terraform.io/stackitcloud/stackit" { + version = "0.66.0" + constraints = "0.66.0" + hashes = [ + "h1:Rf52zPByfdn9Jp6SeEkYD/E7zguGO2/PQy9Z4uuEReU=", + "zh:023b37a0beffd79e06f2dd4813b9809ed75250ffa3525ffad70a2cda2af2b577", + "zh:0922a3494cccea8461f4541cd9305cb2fcf978dfa11b88611a1770e38d42e14a", + "zh:0dde99e7b343fa01f8eefc378171fb8621bedb20f59157d6cc8e3d46c738105f", + "zh:210ca04492487567a7e748cc2f6df717b3c7381760b2210ce18c7eef17ca0391", + "zh:217f697a94f7211beba9b70badbba2a5412f68b3c67fd0174ddea4a8e9c4b14c", + "zh:2bacee9b3d2a5c05b173c6b69d4ec3730046678081b5c0e458728486181f97ec", + "zh:33223720e511654a1f6b7b5b84c81ea7821bff3c8c408fc997a144200a03bdc0", + "zh:5e23084f657f6db8c758384cd4c94c1abacced35eb72e5f8479d265642aa16ce", + "zh:70049797540eaf6299e68c979eb13ca61d860a10d7a2dfc05fc8f083d8862b83", + "zh:7cdf18085cc7f57f79f9b6ee6bdb2749fc693d7b4bc1da4d055901aea4a203d4", + "zh:ae25ba45b3aa94104d148819949592065661f290a7bb0d2cdb7f01264eb771ac", + "zh:babce6253e54d8d4b112fe9ce93517525f18264b74541e50e789e7192392f59f", + "zh:c467940d4d73c5af753943b3448d82b151f07ed373cae223a4d9c8eb491bfa7a", + "zh:e9811458bfc1f31403efc279fce4adca25831dbd192272bdb45da41d38d19c0e", + "zh:eea31631fbff9676c7b67b5698d46b4bfc6e175e914a6ca39abaa774d38cc880", + ] +} diff --git a/000-backend.tf b/000-backend.tf new file mode 100644 index 0000000..41e0572 --- /dev/null +++ b/000-backend.tf @@ -0,0 +1 @@ +terraform {} \ No newline at end of file diff --git a/010-provider.tf b/010-provider.tf new file mode 100644 index 0000000..f968fc9 --- /dev/null +++ b/010-provider.tf @@ -0,0 +1,53 @@ +terraform { + required_providers { + stackit = { + source = "stackitcloud/stackit" + version = "0.66.0" + } + random = { + source = "hashicorp/random" + version = "3.7.2" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = ">=2.25.2" + } + helm = { + source = "hashicorp/helm" + version = "2.17.0" + } + } +} + +provider "stackit" { + default_region = var.stackit_region + service_account_key_path = var.stackit_service_account_key_path + enable_beta_resources = true + experiments = ["iam"] +} + +provider "kubernetes" { + host = yamldecode(stackit_ske_kubeconfig.ske_kubeconfig_01.kube_config).clusters.0.cluster.server + client_certificate = base64decode(yamldecode(stackit_ske_kubeconfig.ske_kubeconfig_01.kube_config).users.0.user.client-certificate-data) + client_key = base64decode(yamldecode(stackit_ske_kubeconfig.ske_kubeconfig_01.kube_config).users.0.user.client-key-data) + cluster_ca_certificate = base64decode(yamldecode(stackit_ske_kubeconfig.ske_kubeconfig_01.kube_config).clusters.0.cluster.certificate-authority-data) +} + +provider "helm" { + kubernetes { + host = yamldecode(stackit_ske_kubeconfig.ske_kubeconfig_01.kube_config).clusters.0.cluster.server + client_certificate = base64decode(yamldecode(stackit_ske_kubeconfig.ske_kubeconfig_01.kube_config).users.0.user.client-certificate-data) + client_key = base64decode(yamldecode(stackit_ske_kubeconfig.ske_kubeconfig_01.kube_config).users.0.user.client-key-data) + cluster_ca_certificate = base64decode(yamldecode(stackit_ske_kubeconfig.ske_kubeconfig_01.kube_config).clusters.0.cluster.certificate-authority-data) + } +} + +provider "vault" { + address = "https://prod.sm.eu01.stackit.cloud" + skip_child_token = true + + auth_login_userpass { + username = stackit_secretsmanager_user.user.username + password = stackit_secretsmanager_user.user.password + } +} \ No newline at end of file diff --git a/020-variables.tf b/020-variables.tf new file mode 100644 index 0000000..010102e --- /dev/null +++ b/020-variables.tf @@ -0,0 +1,14 @@ +variable "stackit_project_id" { + type = string + default = "d75e6aab-b616-4b42-ae3b-aaf161ad626d" +} + +variable "stackit_region" { + type = string + default = "eu01" +} + +variable "stackit_service_account_key_path" { + type = string + default = "./keys/stackit-sa.json" +} diff --git a/030-ske.tf b/030-ske.tf new file mode 100644 index 0000000..2e812cb --- /dev/null +++ b/030-ske.tf @@ -0,0 +1,35 @@ +resource "stackit_ske_cluster" "ske_cluster_01" { + project_id = var.stackit_project_id + name = "secret-test" + kubernetes_version_min = "1.33" + + maintenance = { + enable_kubernetes_version_updates = true + enable_machine_image_version_updates = true + start = "01:00:00Z" + end = "02:00:00Z" + } + + node_pools = [ + { + name = "standard" + machine_type = "g2i.4" + minimum = "3" + maximum = "9" + max_surge = "3" + availability_zones = ["eu01-1", "eu01-2", "eu01-3"] + os_version_min = "4230.2.1" + os_name = "flatcar" + volume_size = 32 + volume_type = "storage_premium_perf6" + }, + ] +} + +resource "stackit_ske_kubeconfig" "ske_kubeconfig_01" { + project_id = var.stackit_project_id + cluster_name = stackit_ske_cluster.ske_cluster_01.name + refresh = true + + depends_on = [stackit_ske_cluster.ske_cluster_01] +} \ No newline at end of file diff --git a/040-secrets-manager.tf b/040-secrets-manager.tf new file mode 100644 index 0000000..0e259f6 --- /dev/null +++ b/040-secrets-manager.tf @@ -0,0 +1,11 @@ +resource "stackit_secretsmanager_instance" "instance" { + project_id = var.stackit_project_id + name = "external-secrets-test" +} + +resource "stackit_secretsmanager_user" "user" { + project_id = var.stackit_project_id + description = "external-secrets-test" + instance_id = stackit_secretsmanager_instance.instance.instance_id + write_enabled = true +} \ No newline at end of file diff --git a/050-random-secret.tf b/050-random-secret.tf new file mode 100644 index 0000000..5ccb0fd --- /dev/null +++ b/050-random-secret.tf @@ -0,0 +1,20 @@ +resource "random_password" "this" { + length = 16 + special = true + override_special = "!#$%&*()-_=+[]{}<>:?" +} + +resource "vault_kv_secret_v2" "random_secret" { + count = 1 + mount = stackit_secretsmanager_instance.instance.instance_id + name = "random-secret" + cas = 1 + delete_all_versions = true + data_json = jsonencode( + { + admin = random_password.this.result + } + ) + + depends_on = [stackit_secretsmanager_user.user] +} \ No newline at end of file diff --git a/060-external-secrets-operator.tf b/060-external-secrets-operator.tf new file mode 100644 index 0000000..1b29132 --- /dev/null +++ b/060-external-secrets-operator.tf @@ -0,0 +1,16 @@ +resource "kubernetes_namespace" "external_secrets" { + metadata { + name = "external-secrets" + } +} + +resource "helm_release" "external_secrets_operator_chart" { + name = "external-secrets-operator" + namespace = kubernetes_namespace.external_secrets.metadata.0.name + repository = "https://charts.external-secrets.io" + chart = "external-secrets" + version = "0.19.2" + lint = true + + values = [] +} \ No newline at end of file diff --git a/070-cluster-secret-store.tf b/070-cluster-secret-store.tf new file mode 100644 index 0000000..303932b --- /dev/null +++ b/070-cluster-secret-store.tf @@ -0,0 +1,41 @@ +resource "kubernetes_secret" "vault_password" { + depends_on = [helm_release.external_secrets_operator_chart] + metadata { + name = "stackit-secretsmanager-user-password" + namespace = kubernetes_namespace.external_secrets.metadata.0.name + } + data = { + username = stackit_secretsmanager_user.user.username + password = stackit_secretsmanager_user.user.password + } +} + +resource "kubernetes_manifest" "stackit_secrets_store" { + manifest = { + apiVersion = "external-secrets.io/v1" + kind = "ClusterSecretStore" + metadata = { + name = "stackit-secrets-store" + } + spec = { + provider = { + vault = { + server = "https://prod.sm.eu01.stackit.cloud" + path = stackit_secretsmanager_instance.instance.instance_id + version = "v2" + auth = { + userPass = { + path = "userpass" + username = stackit_secretsmanager_user.user.username + secretRef = { + namespace = kubernetes_secret.vault_password.metadata.0.namespace + name = kubernetes_secret.vault_password.metadata.0.name + key = "password" + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/080-external-secret.tf b/080-external-secret.tf new file mode 100644 index 0000000..fe632d6 --- /dev/null +++ b/080-external-secret.tf @@ -0,0 +1,37 @@ +resource "kubernetes_namespace" "dev" { + metadata { + name = "dev" + } +} + +resource "kubernetes_manifest" "random_secret_sync" { + manifest = { + apiVersion = "external-secrets.io/v1" + kind = "ExternalSecret" + metadata = { + name = "random-secret-sync" + namespace = kubernetes_namespace.dev.metadata.0.name + } + spec = { + refreshInterval = "30s" + secretStoreRef = { + name = "stackit-secrets-store" + kind = "ClusterSecretStore" + } + target = { + name = "random-secret-sync" + creationPolicy = "Owner" + } + data = [ + { + secretKey = "admin" + remoteRef = { + key = "random-secret" + property = "admin" + } + } + ] + } + } + depends_on = [helm_release.external_secrets_operator_chart] +} \ No newline at end of file