Compare commits

..

33 commits

Author SHA1 Message Date
d361fa8da9 add edits to notification policy 2025-11-26 10:32:30 +01:00
6909208505 enable edits to contact-points 2025-11-26 10:23:57 +01:00
06e9a5a586
Merge pull request 'feature/add-mongodb-instance-options' (#9) from feature/add-mongodb-instance-options into main
Reviewed-on: #9
Reviewed-by: Stanislav_Kopp <stanislav.kopp@mail.schwarz>
2025-11-05 13:03:09 +00:00
c23b03d9c5
cleanup 2025-11-04 17:38:12 +01:00
68a6e7b5a8 add new option variables 2025-11-04 13:14:40 +01:00
a558573190
Merge pull request 'Bump stackit version to 0.68.0' (#8) from bump-version into main
Reviewed-on: #8
2025-10-15 07:44:10 +00:00
3909eeab7b Bump stackit version to 0.68.0 2025-10-15 09:32:20 +02:00
4db34d12d8
Merge pull request 'multiple_dbs_per_psql_instance' (#7) from multiple_dbs_per_psql_instance into main
Reviewed-on: #7
2025-09-08 14:02:13 +00:00
2cade4eba2 finalize changes 2025-09-08 16:00:13 +02:00
3eed77d451 fix attribute 2025-09-08 14:35:40 +02:00
09bff53f30 test output 2025-09-08 14:27:49 +02:00
d928465802 add credential handling for multiple user/dbs 2025-09-08 14:03:42 +02:00
9539b43f7d dafine list for user and db in postgbres module 2025-09-08 09:30:45 +02:00
3318babf7c
Merge pull request 'add node os version' (#6) from add-node-version into main
Reviewed-on: #6
Reviewed-by: Stanislav_Kopp <stanislav.kopp@mail.schwarz>
2025-09-02 19:14:57 +00:00
a6c34ee9bc add node os version 2025-09-02 18:08:11 +02:00
a668f3ad93
Merge pull request 'add k8s min version' (#5) from add-k8s-version into main
Reviewed-on: #5
Reviewed-by: Stanislav_Kopp <stanislav.kopp@mail.schwarz>
2025-09-02 15:04:34 +00:00
9604dfb6f4 add k8s min version 2025-09-02 16:56:31 +02:00
cec9aff5e4 Merge pull request 'STACKITCIN-311 Adjust Grafana TF module to allow Postgres datasources' (#4) from STACKITCIN-311-grafana-postgres-ds into main
Reviewed-on: #4
2025-08-27 07:48:40 +00:00
d5a0c28c11 format 2025-08-27 09:44:37 +02:00
2ffaa5060e Fixes 2025-08-26 18:38:36 +02:00
ae40c80195 STACKITCIN-311 Adjust Grafana TF module to allow Postgres datasources 2025-08-26 18:33:19 +02:00
2bf17ea21e feature(iac): improve readme 2025-08-25 10:01:14 +00:00
6446a9d123 feature(iac): fix service-account module 2025-08-25 10:01:14 +00:00
61761c438a feature(iac): add service-account module 2025-08-25 10:01:14 +00:00
fcbd49b302 feature(iac): enable dns module to create one or many zones with none or many records 2025-08-25 10:00:17 +00:00
0fc20b8741 Update dns/README.md 2025-08-25 10:00:17 +00:00
0839a8adc4 feature(iac): add dns module 2025-08-25 10:00:17 +00:00
4fbc356cbb STACKITCIN-299 Hotfix add lifecycle 2025-08-19 15:50:46 +02:00
77a36b4bb0 STACKITCIN-299 Add support for derivedFields to Grafana TF module 2025-08-19 15:38:33 +02:00
06b2db44bb change version constraint for ske module 2025-08-14 15:50:38 +02:00
4de6f8a4df up README 2025-08-05 19:58:09 +02:00
92e4465889 updated README 2025-08-05 19:55:24 +02:00
7413af8b2b updated grafana modules 2025-08-05 16:40:48 +02:00
41 changed files with 1100 additions and 232 deletions

160
dns/README.md Normal file
View file

@ -0,0 +1,160 @@
# Terraform STACKIT DNS Zone and Record Set Module
This module allows you to declaratively manage DNS zones and their associated record sets in STACKIT.
It supports:
- Creating one or more new DNS zones.
- Using pre-existing DNS zones by providing a `zone_id`.
- Creating one or more record sets within any managed zone.
## Usage Example
Here is an example of how to use the module to create one or many new zones and use another pre-existing one.
```terraform
terraform {
required_providers {
stackit = {
source = "stackitcloud/stackit"
version = "~> 0.59.0"
}
}
backend "s3" {
endpoints = {
s3 = "https://object.storage.eu01.onstackit.cloud"
}
bucket = "test-bucket"
key = "state/test/dns"
region = "eu01"
skip_region_validation = true
skip_metadata_api_check = true
skip_credentials_validation = true
skip_requesting_account_id = true
skip_s3_checksum = true
use_path_style = true
}
}
provider "stackit" {
# Configure your provider credentials, i.e.:
default_region = local.region
enable_beta_resources = true
service_account_key_path = "sa_key.json"
}
module "dns" {
source = "./path/to/your/module" # Or a Git URL git::https://commerce-platform.git.onstackit.cloud/commerce-platform-public//terraform-modules/dns
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
zones = {
# EXAMPLE 1: Create multiple zones with multiple A records
"primary_domain" = {
name = "first domain"
dns_name = "test-b.stackit.rocks"
record_sets = {
"qa-test" = {
name = "qa-test"
type = "A"
ttl = 3600
records = ["192.0.1.1", "192.0.1.2"]
}
"beta-test" = {
name = "beta-test"
type = "A"
ttl = 3600
records = ["192.0.2.10", "192.0.2.20", "192.0.2.30"]
}
}
},
"secondary_domain" = {
name = "second domain"
dns_name = "test-a.stackit.rocks"
record_sets = {
"alpha-records" = {
name = "alpha-test"
type = "A"
ttl = 3600
records = ["192.10.2.10", "192.10.2.20", "192.10.2.30"]
}
}
},
# EXAMPLE 2: Use a pre-existing zone and add a new TXT and A record to it
"existing_domain" = {
zone_id = "zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz"
record_sets = {
"spf_txt" = {
name = "@"
type = "TXT"
records = ["v=spf1 mx -all"]
ttl = 7200
comment = "this is a test txt record"
},
"spf_cname" = {
name = "exampledomain"
type = "A"
ttl = 3600
records = ["192.0.29.1"]
}
}
},
# EXAMPLE 3: Create a new zone with no initial records
"empty_domain" = {
name = "My Empty Domain"
dns_name = "empty-example.com"
}
}
}
```
## Inputs
| Variable Name | Description | Type | Required |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------- |
| `project_id` | STACKIT project ID to which the DNS resources are associated. | `string` | Yes |
| `zones` | A map of DNS zones to be created or managed. Each zone contains a `name`, `dns_name`, and `record_sets` map. | `map` | Yes |
| `record_sets` | A map of DNS record sets to create within this zone. Each record set contains a `name`, `type`, `records`, `ttl`, `comment`, and `active` attribute. | `map` | Optional |
### Values for zones
| Key | Description | Type | Required |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------- | ----------------------------------- |
| `zone_id` | The ID of an existing DNS zone to manage. If provided, the module will use the existing zone. If not provided, a new zone will be created. | `string` | Optional |
| `name` | The descriptive name of the DNS zone. | `string` | Optional |
| `dns_name` | The DNS name of the DNS zone. | `string` | Optional |
| `contact_email` | The contact email for the DNS zone. | `string` | Optional |
| `description` | A description of the DNS zone. | `string` | Optional |
| `acl` | The Access Control List (ACL) for the DNS zone. | `string` | Optional |
| `active` | Whether the DNS zone is active or not. | `bool` | Optional (currently non-functional) |
| `default_ttl` | The default Time-to-Live (TTL) for records in the DNS zone. | `number` | Optional |
| `expire_time` | The expiration time for the DNS zone. | `number` | Optional |
| `is_reverse_zone` | Whether the DNS zone is a reverse zone or not. | `bool` | Optional |
| `negative_cache` | The negative cache duration for the DNS zone. | `number` | Optional |
| `primaries` | A list of primary name servers for the DNS zone. | `list(string)` | Optional |
| `refresh_time` | The refresh time for the DNS zone. | `number` | Optional |
| `retry_time` | The retry time for the DNS zone. | `number` | Optional |
| `type` | The type of the DNS zone. Defaults to "primary" if not provided. | `string` | Optional |
### Values for record_sets
| Key | Description | Type | Required |
| --------- | ------------------------------------------------------------- | -------------- | ----------------------------------- |
| `name` | The name of the DNS record set. | `string` | Yes |
| `type` | The type of the DNS record set. | `string` | Yes |
| `records` | A list of DNS records for the record set. | `list(string)` | Yes |
| `ttl` | The Time-to-Live (TTL) for the DNS records in the record set. | `number` | Optional |
| `comment` | A comment for the DNS record set. | `string` | Optional |
| `active` | Whether the DNS record set is active or not. | `bool` | Optional (currently non-functional) |
## Outputs
| Name | Description |
| ------------- | ------------------------------------------------------------------------------------------ |
| `zones` | A map of all managed DNS zone objects, including those created and those referenced by ID. |
| `record_sets` | A map of all created DNS record set objects. |
## Notes
Setting a zone or record to inactive by using `active = false` is currently not possible due to a bug in the provider. It is active by default.

94
dns/dns.tf Normal file
View file

@ -0,0 +1,94 @@
# main.tf
# --------------------------------------------------------------------------------------------------
# LOCAL VARIABLES
# --------------------------------------------------------------------------------------------------
locals {
# Create a map of zones to be created (where zone_id is not specified)
zones_to_create = { for k, v in var.zones : k => v if try(v.zone_id, null) == null }
# Create a map of zones to be referenced via data source (where zone_id is specified)
zones_to_read = { for k, v in var.zones : k => v if try(v.zone_id, null) != null }
# Merge the created resources and data sources into a single, unified map.
# This allows record sets to reference a zone regardless of whether it was created or read.
all_zones = merge(
{
for k, zone in stackit_dns_zone.this : k => zone
},
{
for k, zone in data.stackit_dns_zone.this : k => zone
}
)
# Flatten the nested record_sets structure into a single list, making it easy to iterate with for_each.
# Each item in the list retains a reference to its parent zone key.
flat_record_sets = flatten([
for zone_key, zone_config in var.zones : [
for record_key, record_config in try(zone_config.record_sets, {}) : {
zone_key = zone_key
record_key = record_key
name = record_config.name
type = record_config.type
records = record_config.records
ttl = try(record_config.ttl, null)
comment = try(record_config.comment, null)
active = try(record_config.active, null)
}
]
])
}
# --------------------------------------------------------------------------------------------------
# DNS ZONE RESOURCES (CREATE OR READ)
# --------------------------------------------------------------------------------------------------
# Create new DNS zones for configurations that do not have a zone_id
resource "stackit_dns_zone" "this" {
for_each = local.zones_to_create
project_id = var.project_id
name = each.value.name
dns_name = each.value.dns_name
contact_email = try(each.value.contact_email, null)
description = try(each.value.description, null)
acl = try(each.value.acl, null)
active = try(each.value.active, null)
default_ttl = try(each.value.default_ttl, null)
expire_time = try(each.value.expire_time, null)
is_reverse_zone = try(each.value.is_reverse_zone, null)
negative_cache = try(each.value.negative_cache, null)
primaries = try(each.value.primaries, null)
refresh_time = try(each.value.refresh_time, null)
retry_time = try(each.value.retry_time, null)
type = try(each.value.type, "primary")
}
# Read existing DNS zones for configurations that provide a zone_id
data "stackit_dns_zone" "this" {
for_each = local.zones_to_read
project_id = var.project_id
zone_id = each.value.zone_id
}
# --------------------------------------------------------------------------------------------------
# DNS RECORD SET RESOURCES
# --------------------------------------------------------------------------------------------------
resource "stackit_dns_record_set" "this" {
# The key is a unique combination of the zone and record keys for a stable address.
for_each = { for record in local.flat_record_sets : "${record.zone_key}.${record.record_key}" => record }
project_id = var.project_id
# Look up the correct zone_id from the unified 'all_zones' map
zone_id = local.all_zones[each.value.zone_key].zone_id
name = each.value.name
type = each.value.type
records = each.value.records
ttl = each.value.ttl
comment = each.value.comment
active = each.value.active
}

11
dns/outputs.tf Normal file
View file

@ -0,0 +1,11 @@
# outputs.tf
output "zones" {
description = "A map of all managed DNS zone objects, including those created and those referenced by ID."
value = local.all_zones
}
output "record_sets" {
description = "A map of all created DNS record set objects."
value = stackit_dns_record_set.this
}

8
dns/providers.tf Normal file
View file

@ -0,0 +1,8 @@
terraform {
required_providers {
stackit = {
source = "stackitcloud/stackit"
version = "~> 0.68.0"
}
}
}

48
dns/variables.tf Normal file
View file

@ -0,0 +1,48 @@
# variables.tf
variable "project_id" {
type = string
description = "STACKIT project ID to which the DNS resources are associated."
}
variable "zones" {
type = map(object({
# If zone_id is provided, the module will use the existing zone.
# Otherwise, a new zone will be created using the attributes below.
zone_id = optional(string)
# Required attributes for new zones
name = optional(string)
dns_name = optional(string)
# Optional attributes for new zones
contact_email = optional(string)
description = optional(string)
acl = optional(string)
active = optional(bool)
default_ttl = optional(number)
expire_time = optional(number)
is_reverse_zone = optional(bool)
negative_cache = optional(number)
primaries = optional(list(string))
refresh_time = optional(number)
retry_time = optional(number)
type = optional(string, "primary")
# A map of DNS record sets to create within this zone.
# The key is a logical name for the record set (e.g., "www", "mx_record").
record_sets = optional(map(object({
# Required record set attributes
name = string
type = string
records = list(string)
# Optional record set attributes
ttl = optional(number)
comment = optional(string)
active = optional(bool, true)
})), {})
}))
description = "A map of DNS zones to manage. The key is a logical name for the zone."
default = {}
}

View file

@ -1,83 +1,252 @@
# Modules for Grafana alerts and dashboards
<!-- TOC -->
## Alerting
Please check documentation about Grafana alerting [here](https://itdoc.schwarz/x/X11nf) and [official documentation](https://grafana.com/docs/grafana/latest/alerting/) for deeper look.
The Terraform modules are separated per resource type, check README in each module directory for spefic examples.
Below is example for alerts using „**Prometheus/Thanos**“ datasorce and sending notification to „**Google Chat**“.
Below is example for alerts using „**Prometheus/Thanos**“ datasource and sending notification to „**Google Chat**“.
## Authentication
Set Grafana credentials as Terraform variables:
```hcl title="main.tf"
# Datasource
```bash
export TF_VAR_grafana_url="https://grafana.example.com"
export TF_VAR_grafana_username="admin"
export TF_VAR_grafana_password="super-secret"
```
These credentials are used by all modules to authenticate with the Grafana API.
---
## Directory Structure
Organize alerts, templates, and Terraform code as follows:
```
.
├── alerts/
│ ├── common-infra/
│ │ ├── loki/
│ │ │ └── alert-loki.yaml
│ │ └── thanos/
│ │ └── alert-thanos.yaml
│ ├── oncall/
│ │ └── alert-oncall.yaml
│ └── heartbeats/
│ └── alert-heartbeat.yaml
├── templates/
│ └── myteam/
│ └── gchat-message.tmpl
└── main.tf
```
- **Alerts**: YAML files defining rule groups (`apiVersion: 1, groups: [...]`).
- **Templates**: Notification templates for Google Chat contact points.
- **Terraform code**: References modules and binds everything together.
---
## Defining Secrets
Datasource URLs and credentials should be stored in Terraform variables, not hardcoded.
**Example: Environment Variables**
```bash
export TF_VAR_thanos_coin_prd_url="https://thanos.example.com"
export TF_VAR_thanos_coin_prd_user="reader"
export TF_VAR_thanos_coin_prd_pass="password"
export TF_VAR_loki_coin_prd_url="https://loki.example.com"
export TF_VAR_loki_coin_prd_user="reader"
export TF_VAR_loki_coin_prd_pass="password"
export TF_VAR_opsgenie_api_key="xxxxxx"
```
---
## Module Usage
### Datasources
Define multiple datasources (Prometheus, Loki, etc.) with unique keys for URL/username/password:
```hcl
module "datasource" {
source = "git::https://commerce-platform.git.onstackit.cloud/commerce-platform-public/terraform-modules//grafana/datasource?ref=main"
datasource_name = "Thanos - Myteam"
datasource_url = var.datasource_url
datasource_username = var.datasource_username
datasource_password = var.datasource_password
}
# Alert Receiver / Contact Point
module "gchat-contact-point" {
datasources = {
Thanos-Common-Infra-PRD = {
type = "prometheus"
url_key = "thanos_coin_prd"
basic_auth_user_key = "thanos_coin_prd"
pass_key = "thanos_coin_prd"
is_default = true
}
Loki-Common-Infra-PRD = {
type = "loki"
url_key = "loki_coin_prd"
basic_auth_user_key = "loki_coin_prd"
pass_key = "loki_coin_prd"
}
}
datasource_urls = {
thanos_coin_prd = var.thanos_coin_prd_url
loki_coin_prd = var.loki_coin_prd_url
}
datasource_users = {
thanos_coin_prd = var.thanos_coin_prd_user
loki_coin_prd = var.loki_coin_prd_user
}
datasource_passwords = {
thanos_coin_prd = var.thanos_coin_prd_pass
loki_coin_prd = var.loki_coin_prd_pass
}
}
```
### Contact Points
**Google Chat**
Each Google Chat space is configured as a contact point:
```hcl
module "gchat-contact-point-coin" {
source = "git::https://commerce-platform.git.onstackit.cloud/commerce-platform-public/terraform-modules//grafana/contact-point-gchat?ref=main"
gchat-url = var.google_chat_url
contact-point-name = "gchat"
gchat_url = var.gchat_url_coin
contact_point_name = "gchat-coin"
templates_dir = "templates/coin"
template_prefix = "coin-"
disable_provenance = true
}
```
**OpsGenie**
# Alert Rule Folders
OpsGenie contact points use API keys:
```hcl
module "opsgenie-contact-point" {
source = "git::https://commerce-platform.git.onstackit.cloud/commerce-platform-public/terraform-modules//grafana/contact-point-opsgenie?ref=main"
contact_point_name = "opsgenie-dev"
opsgenie_api_key = var.opsgenie_api_key
}
```
### Alert Folders
Organize alerts in Grafana folders for logical separation:
```hcl
module "alert-folder" {
source = "git::https://commerce-platform.git.onstackit.cloud/commerce-platform-public/terraform-modules//grafana/alert-folder?ref=main"
alert-folder = "Alerts"
source = "git::https://commerce-platform.git.onstackit.cloud/commerce-platform-public/terraform-modules//grafana/alert-folder?ref=main"
alert-folder = "Common-Infra-Alerts"
}
```
### Notification Policies
# Template for messages
module "message-templates" {
source = "git::https://commerce-platform.git.onstackit.cloud/commerce-platform-public/terraform-modules//grafana/message-template?ref=main"
templates_dir = "templates"
disable_provenance = true
}
Map folders to contact points (e.g., send “Common-Infra-Alerts” to Google Chat):
# Notification policies
```hcl
module "notification-policy" {
source = "git::https://commerce-platform.git.onstackit.cloud/commerce-platform-public/terraform-modules//grafana/notification-policy?ref=main"
default_contact_point_uid = module.gchat-contact-point.contact_name
default_contact_point_uid = module.gchat-contact-point-coin.contact_point_name
group_by = ["alertname"]
folder_policies = {
"Alerts" = module.gchat-contact-point.contact_name
"Common-Infra-Alerts" = module.gchat-contact-point-coin.contact_point_name
"Common-Infra-OnCall-Alerts" = module.opsgenie-contact-point.contact_name
}
}
```
# Alert definition
module "alerting" {
source = "git::https://commerce-platform.git.onstackit.cloud/commerce-platform-public/terraform-modules//grafana/alerts?ref=main"
### Alert Definitions
alerts_dir = "alerts"
default_datasource_uid = module.datasource.datasource_uid
default_receiver = module.gchat-contact-point.contact_name
default_folder_uid = module.alert-folder.folder_uid
default_interval_seconds = 60
disable_provenance = true
Alert rules are defined in YAML and applied via the module:
```hcl
module "alerting-coin" {
source = "git::https://commerce-platform.git.onstackit.cloud/commerce-platform-public/terraform-modules//grafana/alerts?ref=main"
alerts_dir = "alerts/common-infra/thanos"
datasource_uid = module.datasource.datasource_uids["Thanos-Common-Infra-PRD"]
folder_uid = module.alert-folder.folder_uid
receiver = module.gchat-contact-point-coin.contact_point_name
disable_provenance = true
}
```
With this configuration you need to place your notification templates into `templates` folder and alert definitions in YAML format to `alerts` folder in same directoty where `main.tf`is located.
You can example for both in [examples](./examples/) folder.
For this example you need export your secret variables e.g. with lookup in Secret Manager.
```sh
export TF_VAR_grafana_url="<GRAFANA URL>"
export TF_VAR_grafana_username="admin"
export TF_VAR_grafana_password="xxxxxxx"
export TF_VAR_google_chat_url="https://chat.googleapis.com/v1/spaces/xxxxx"
export TF_VAR_datasource_url="https://xxxxx.stackit.cloud/instances/xxxxxx"
export TF_VAR_datasource_username="stackit9_xxxxx"
export TF_VAR_datasource_password="xxxxxx"
---
## Alert YAML Format
Alerts are defined in YAML Grafana format. The easiest way to get example from scratch is to define alert in Grafana UI and then export it using „Export rules“ button.
However, make sure to remove some fields which are not needed and provided by Terraform module logic automatically:
- `datasourceUid` (defined with `alerts` module)
- `notification_settings` ( defined with `notification policy` module)
Each file must have `apiVersion: 1` and define groups:
```yaml
apiVersion: 1
groups:
- name: infra-alerts
interval: 1m
rules:
- uid: pod-restart-alert
title: "Pod Restart Count High"
condition: C
data:
- refId: A
relativeTimeRange:
from: 600
to: 0
model:
expr: increase(kube_pod_container_status_restarts_total{}[5m]) > 3
instant: true
refId: A
- refId: C
datasourceUid: __expr__
model:
conditions:
- evaluator:
params: [0]
type: gt
operator:
type: and
query:
params: [C]
reducer:
type: last
type: query
expression: A
type: threshold
noDataState: OK
execErrState: Error
for: 5m
annotations:
description: "Pod is restarting too often"
```
## Dashboard
TODO
---
## Updating Alerts or Contact Points
- Add new YAML files under `alerts/` for additional rules.
- Add new modules in `main.tf` for new datasources or contact points.
- Run `terraform apply` to sync changes to Grafana.
You can examples for alerts and templates in [examples](./examples/) folder.

View file

@ -1,10 +1,8 @@
locals {
#################################################################
# 1. Discover & decode alert YAML files from <root>/<alerts_dir>
#################################################################
# 1. Find and decode YAML files
alert_files = fileset(
"${path.root}/${var.alerts_dir}",
var.file_pattern
"**/*.yaml"
)
decoded_files = [
@ -12,31 +10,36 @@ locals {
yamldecode(file("${path.root}/${var.alerts_dir}/${f}"))
]
#################################################################
# 2. Flatten: each file may contain multiple groups
#################################################################
# 2. Flatten: each YAML may define multiple groups
groups_raw = flatten([
for doc in local.decoded_files : try(doc.groups, [])
])
#################################################################
# 3. Merge defaults & convert camelCase → snake_case
#################################################################
# 3. Inject defaults (datasource, folder, interval) and sanitize models
groups = [
for g in local.groups_raw : merge(g, {
uid = try(
g.uid,
trim(replace(replace(lower(g.name), " ", "-"), "_", "-"), "-")
)
folder_uid = try(try(g.folder_uid, g.folder), var.default_folder_uid)
interval = try(g.interval, "1m")
uid = try(g.uid, lower(replace(g.name, " ", "-")))
folder_uid = var.folder_uid
interval = try(g.interval, "${var.default_interval_seconds}s")
rules = [
for r in g.rules : merge(r, {
data = [
for d in r.data : merge(d, {
datasource_uid = try(d.datasourceUid, var.default_datasource_uid)
# Preserve __expr__ for expressions, otherwise inject default UID
datasource_uid = (
try(d.datasourceUid, "") == "__expr__"
? "__expr__"
: var.datasource_uid
)
relative_time_range = try(d.relativeTimeRange, { from = 600, to = 0 })
# Sanitize model: remove keys Grafana ignores to prevent plan drift
model = {
for k, v in d.model : k => v
if !(k == "queryType" || k == "query_type")
}
})
]
})

View file

@ -1,8 +1,5 @@
resource "grafana_rule_group" "this" {
for_each = {
for g in local.groups :
g.uid => g
}
for_each = { for g in local.groups : g.uid => g }
name = each.value.name
folder_uid = each.value.folder_uid
@ -21,14 +18,15 @@ resource "grafana_rule_group" "this" {
uid = rule.value.uid
name = try(rule.value.title, rule.value.name)
condition = rule.value.condition
for = try(rule.value.for, null)
no_data_state = rule.value.noDataState
exec_err_state = rule.value.execErrState
is_paused = try(rule.value.isPaused, false)
for = try(rule.value.for, null)
labels = try(rule.value.labels, {})
# Merge team labels with platform-injected receiver
annotations = try(rule.value.annotations, {})
labels = try(rule.value.labels, {})
dynamic "data" {
for_each = rule.value.data

View file

@ -2,3 +2,10 @@ output "rule_group_ids" {
description = "Map: <folder_uid>.<name> → Grafana rule-group ID"
value = { for k, v in grafana_rule_group.this : k => v.id }
}
output "debug_alert_files" {
value = local.alert_files
}
output "debug_groups" {
description = "Parsed groups from YAML"
value = local.groups
}

View file

@ -4,35 +4,29 @@ variable "alerts_dir" {
description = "Relative path to the directory containing alert rule YAML files."
}
variable "file_pattern" {
variable "datasource_uid" {
description = "Grafana datasource UID to apply to all alerts in this module"
type = string
default = "*.y{a,}ml"
description = "Glob pattern to match alert rule YAML files (e.g. *.yaml, *.yml)."
}
variable "default_datasource_uid" {
variable "folder_uid" {
description = "Grafana folder UID where alerts will be placed"
type = string
description = "UID of the Prometheus or Thanos datasource to use if not specified in the alert rule."
}
variable "default_receiver" {
variable "receiver" {
description = "Contact point name to associate with alerts"
type = string
description = "Name of the contact point (receiver) to use for notifications if not defined in alert rule."
}
variable "default_folder_uid" {
type = string
description = "UID of the Grafana folder to use for alert rules when not defined in the YAML."
}
variable "default_interval_seconds" {
description = "Default evaluation interval (in seconds)"
type = number
default = 60
description = "Default evaluation interval (in seconds) for alert rule groups if not set in YAML."
}
variable "disable_provenance" {
description = "Disable provenance flag for imported alerts"
type = bool
default = false
description = "If true, disables Grafana alert provisioning provenance (sets disable_provenance = true)."
}

View file

@ -1,9 +1,10 @@
resource "grafana_contact_point" "this" {
name = var.contact-point-name
name = var.contact_point_name
disable_provenance = true
googlechat {
url = var.gchat-url
message = "{{ template \"gchat-body-template\" . }}"
title = "{{ template \"gchat-title-template\" . }}"
url = var.gchat_url
message = "{{ template \"${var.template_prefix}gchat-body-template\" . }}"
title = "{{ template \"${var.template_prefix}gchat-title-template\" . }}"
}
}

View file

@ -1,8 +1,14 @@
output "contact_name" {
output "contact_point_name" {
value = grafana_contact_point.this.name
description = "UID of the created contact point"
description = "Name of the created contact point"
}
output "contact_id" {
output "contact_point_id" {
value = grafana_contact_point.this.id
description = "UID of the created contact point"
description = "ID of the created contact point"
}
output "template_names" {
value = [for t in grafana_message_template.templates : t.name]
description = "List of message template names created by this module"
}

View file

@ -0,0 +1,24 @@
locals {
# Collect template files relative to root
template_files = fileset(
"${path.root}/${var.templates_dir}",
var.file_pattern
)
# Map filename (without extension) to template content
templates = {
for rel_path in local.template_files :
trimsuffix(basename(rel_path), ".tmpl") => {
content = file("${path.root}/${var.templates_dir}/${rel_path}")
}
}
}
resource "grafana_message_template" "templates" {
for_each = local.templates
name = "${var.template_prefix}${each.key}"
template = each.value.content
disable_provenance = var.disable_provenance
}

View file

@ -1,11 +1,32 @@
# Grafana contact point
variable "gchat-url" {
description = "gchat-webhook url"
type = string
sensitive = true
variable "contact_point_name" {
description = "Name of the Grafana contact point"
type = string
}
variable "contact-point-name" {
description = "gchat-contact-point-name"
type = string
variable "gchat_url" {
description = "Google Chat webhook URL"
type = string
}
variable "templates_dir" {
description = "Path to directory containing template files (.tmpl)"
type = string
}
variable "file_pattern" {
description = "Pattern to match template files"
type = string
default = "*.tmpl"
}
variable "disable_provenance" {
description = "Disable provenance for message templates"
type = bool
default = false
}
variable "template_prefix" {
description = "Optional prefix for template names to avoid collisions"
type = string
default = ""
}

View file

@ -1,11 +1,114 @@
# Create the basic "shell" of each datasource.
resource "grafana_data_source" "this" {
type = "prometheus"
name = var.datasource_name
url = var.datasource_url
basic_auth_enabled = true
basic_auth_username = var.datasource_username
for_each = var.datasources
secure_json_data_encoded = jsonencode({
basicAuthPassword = var.datasource_password
})
name = each.key
type = each.value.type
url = var.datasource_urls[each.value.url_key]
is_default = coalesce(each.value.is_default, false)
# For HTTP Basic Auth (Loki, Prometheus, etc.)
basic_auth_enabled = each.value.basic_auth_user_key != null
basic_auth_username = each.value.basic_auth_user_key != null ? var.datasource_users[each.value.basic_auth_user_key] : null
# For database usernames (like Postgres)
# This sets the username initially.
username = each.value.db_user_key != null ? var.datasource_users[each.value.db_user_key] : null
# This resource must ignore attributes that are
# managed by the other 'config' resources below.
lifecycle {
ignore_changes = [
json_data_encoded,
secure_json_data_encoded,
# Also ignore username, as it can be managed/reported back differently by the API.
username,
]
}
}
# Apply the main json_data for datasources like PostgreSQL.
resource "grafana_data_source_config" "json_data_main" {
for_each = {
for k, v in var.datasources : k => v
if v.json_data != null && v.derived_fields == null && v.traces_to_logs == null
}
uid = grafana_data_source.this[each.key].uid
json_data_encoded = jsonencode(each.value.json_data)
# This config must ignore the password, which is managed by the 'passwords' resource.
lifecycle {
ignore_changes = [secure_json_data_encoded]
}
}
# Apply passwords to all datasources that require one.
resource "grafana_data_source_config" "passwords" {
for_each = {
for k, v in var.datasources : k => v if v.pass_key != null
}
uid = grafana_data_source.this[each.key].uid
secure_json_data_encoded = jsonencode(
each.value.type == "grafana-postgresql-datasource" ? {
password = var.datasource_passwords[each.value.pass_key]
} : {
basicAuthPassword = var.datasource_passwords[each.value.pass_key]
}
)
# This config must ignore the main json_data, which is managed elsewhere.
lifecycle {
ignore_changes = [json_data_encoded]
}
}
# Apply Loki-specific 'derivedFields' configuration.
resource "grafana_data_source_config" "loki_derived_fields" {
for_each = {
for k, v in var.datasources : k => v if v.type == "loki" && v.derived_fields != null
}
uid = grafana_data_source.this[each.key].uid
json_data_encoded = jsonencode({
derivedFields = [
for field in each.value.derived_fields : {
datasourceUid = grafana_data_source.this[field.target_datasource_name].uid
matcherRegex = field.matcher_regex
name = field.name
url = field.url
}
]
})
# This config must ignore the password, which is managed elsewhere.
lifecycle {
ignore_changes = [secure_json_data_encoded]
}
}
# Apply Tempo-specific 'tracesToLogsV2' configuration.
resource "grafana_data_source_config" "tempo_traces_to_logs" {
for_each = {
for k, v in var.datasources : k => v if v.type == "tempo" && v.traces_to_logs != null
}
uid = grafana_data_source.this[each.key].uid
json_data_encoded = jsonencode({
tracesToLogsV2 = {
datasourceUid = grafana_data_source.this[each.value.traces_to_logs.target_datasource_name].uid
query = each.value.traces_to_logs.query
customQuery = coalesce(each.value.traces_to_logs.custom_query, true)
filterBySpanID = coalesce(each.value.traces_to_logs.filter_by_span_id, false)
filterByTraceID = coalesce(each.value.traces_to_logs.filter_by_trace_id, false)
spanStartTimeShift = each.value.traces_to_logs.span_start_time_shift
spanEndTimeShift = each.value.traces_to_logs.span_end_time_shift
}
})
# This config must ignore the password, which is managed elsewhere.
lifecycle {
ignore_changes = [secure_json_data_encoded]
}
}

View file

@ -1,3 +1,4 @@
output "datasource_uid" {
value = grafana_data_source.this.uid
output "datasource_uids" {
description = "UIDs of created Grafana datasources"
value = { for k, v in grafana_data_source.this : k => v.uid }
}

View file

@ -1,18 +1,56 @@
# Grafana contact point
variable "datasource_url" {
type = string
variable "datasources" {
description = <<EOT
Map of datasources to create. Keys are datasource names.
Each datasource specifies type, keys to lookup URL/user/password,
and optional configurations for linking data sources.
EOT
type = map(object({
type = string
url_key = string
pass_key = optional(string)
is_default = optional(bool)
# Key to look up a database username
db_user_key = optional(string)
# Key to look up a basic auth username
basic_auth_user_key = optional(string)
# Non-sensitive JSON data for Postgres, etc.
json_data = optional(map(any))
# Linking Attributes (for Loki/Tempo)
derived_fields = optional(list(object({
target_datasource_name = string
matcher_regex = string
name = string
url = string
})))
traces_to_logs = optional(object({
target_datasource_name = string
query = string
custom_query = optional(bool)
filter_by_span_id = optional(bool)
filter_by_trace_id = optional(bool)
span_start_time_shift = optional(string)
span_end_time_shift = optional(string)
}))
}))
}
variable "datasource_name" {
type = string
variable "datasource_urls" {
description = "Map of datasource URLs, keyed by url_key"
type = map(string)
sensitive = true
}
variable "datasource_username" {
type = string
sensitive = true
variable "datasource_users" {
description = "Map of datasource usernames, keyed by db_user_key or basic_auth_user_key"
type = map(string)
sensitive = true
}
variable "datasource_password" {
type = string
sensitive = true
variable "datasource_passwords" {
description = "Map of datasource passwords, keyed by pass_key"
type = map(string)
sensitive = true
}

View file

@ -1,4 +1,4 @@
{{ define "gchat-body-template" -}}
{{ define "google-chat-body-template" -}}
{{- $alerts := .Alerts }}
{{- if not $alerts }}{{ $alerts = . }}{{ end }}

View file

@ -1,4 +1,4 @@
{{ define "gchat-title-template" -}}
{{ define "google-chat-title-template" -}}
{{- if eq .Status "firing" -}}
🔥 Firing:
{{- else if eq .Status "resolved" -}}

View file

@ -1,6 +1,7 @@
resource "grafana_notification_policy" "this" {
contact_point = var.default_contact_point_uid
group_by = var.group_by
disable_provenance = true
dynamic "policy" {
for_each = var.folder_policies

View file

@ -19,30 +19,3 @@ resource "stackit_mongodbflex_user" "this" {
roles = var.mongodb_user_roles
database = var.mongodb_user_database
}
# // Configure Secret Manager Provider
# provider "vault" {
# address = "https://prod.sm.eu01.stackit.cloud"
# skip_child_token = true
# auth_login_userpass {
# username = var.secret_manager_username
# password = var.secret_manager_password
# }
# }
# // Store MongoDB Credentials in Secret Manager
# resource "vault_kv_secret_v2" "mongodb_cred_save" {
# mount = var.secret_manager_instance_id
# name = var.mongodb_secrets_path
# cas = 1
# delete_all_versions = true
# data_json = jsonencode(
# {
# username = stackit_mongodbflex_user.mongodb_user.username,
# password = stackit_mongodbflex_user.mongodb_user.password,
# host = stackit_mongodbflex_user.mongodb_user.host,
# port = stackit_mongodbflex_user.mongodb_user.port,
# uri = stackit_mongodbflex_user.mongodb_user.uri
# }
# )
# }

View file

@ -2,7 +2,7 @@ terraform {
required_providers {
stackit = {
source = "stackitcloud/stackit"
version = "~> 0.50.0"
version = "~> 0.68.0"
}
}
}

View file

@ -30,7 +30,9 @@ variable "mongodb_instance_flavor" {
variable "mongodb_instance_options" {
description = "options for mongodb"
type = object({
type = string
type = string
snapshot_retention_days = number
point_in_time_window_hours = number
})
}

View file

@ -2,7 +2,7 @@ terraform {
required_providers {
stackit = {
source = "stackitcloud/stackit"
version = "~> 0.50.0"
version = "~> 0.68.0"
}
}
}

View file

@ -2,7 +2,7 @@ terraform {
required_providers {
stackit = {
source = "stackitcloud/stackit"
version = "~> 0.50.0"
version = "~> 0.68.0"
}
grafana = {
source = "grafana/grafana"

View file

@ -3,39 +3,18 @@ output "postgres_instance_id" {
value = stackit_postgresflex_instance.this.instance_id
}
# Postgres Database Output
output "postgres_database_id" {
value = stackit_postgresflex_database.this.database_id
}
# Postgres User Output
output "postgres_host" {
value = stackit_postgresflex_user.this.host
}
output "postgres_password" {
value = stackit_postgresflex_user.this.password
# Postgres Credential Output
output "postgres_credentials" {
value = {
for k, u in stackit_postgresflex_user.this :
k => {
host = u.host
username = u.username
password = u.password
port = u.port
db_name = stackit_postgresflex_database.this[u.username].name
uri = u.uri
}
}
sensitive = true
}
output "postgres_user" {
value = stackit_postgresflex_user.this.username
}
output "postgres_port" {
value = stackit_postgresflex_user.this.port
}
output "postgres_db_name" {
value = stackit_postgresflex_database.this.name
}
output "postgres_uri" {
value = stackit_postgresflex_user.this.uri
sensitive = true
}
output "postgres_user_id" {
value = stackit_postgresflex_user.this.user_id
}

View file

@ -12,18 +12,24 @@ resource "stackit_postgresflex_instance" "this" {
// Postgres User
resource "stackit_postgresflex_user" "this" {
for_each = {
for db in var.postgres_databases : db.user_name => db
}
depends_on = [ stackit_postgresflex_instance.this ]
project_id = var.stackit_project_id
instance_id = stackit_postgresflex_instance.this.instance_id
username = var.postgres_db_user_name
roles = var.postgres_db_user_roles
username = each.value.user_name
roles = each.value.user_roles
}
// Postgres Database
resource "stackit_postgresflex_database" "this" {
depends_on = [ stackit_postgresflex_user.this ]
for_each = {
for db in var.postgres_databases : db.db_name => db
}
depends_on = [stackit_postgresflex_user.this]
project_id = var.stackit_project_id
instance_id = stackit_postgresflex_instance.this.instance_id
name = var.postgres_db_name
owner = var.postgres_db_user_name
name = each.value.db_name
owner = each.value.user_name
}

View file

@ -2,7 +2,7 @@ terraform {
required_providers {
stackit = {
source = "stackitcloud/stackit"
version = "~> 0.50.0"
version = "~> 0.68.0"
}
}
}

67
postgres/readme.md Normal file
View file

@ -0,0 +1,67 @@
# Module for creating Postgres Flex Instance with Databases and Users
## Example
```main.tf
# Postgres Flex Instance
module "postgres-flex" {
source = "git::https://commerce-platform.git.onstackit.cloud/commerce-platform-public/terraform-modules//postgres?ref=main
stackit_project_id = local.stackit_project_id
postgres_instance_name = "example-db"
postgres_instance_replicas = 1
postgres_instance_storage = {
class = "premium-perf2-stackit"
size = 5
}
postgres_instance_flavor = {
cpu = 2
ram = 4
}
postgres_instance_acl = [
"193.148.160.0/19",
"45.129.40.0/21"
]
postgres_instance_backup_schedule = "00 02 * * *"
postgres_instance_version = "17"
postgres_instance_region = "eu01"
postgres_databases = [
{
db_name = "database-a"
user_name = "user-a"
user_roles = ["createdb", "login"]
},
{
db_name = "database-b"
user_name = "user-b"
user_roles = ["createdb", "login"]
},
]
}
# safe credentials
module "postgres-credentials-sm-a" {
source = "git::https://commerce-platform.git.onstackit.cloud/commerce-platform-public/terraform-modules//create-secret?ref=main"
secret_manager_instance_id = local.secret_manager_instance_id
secret_manager_username = var.secret_manager_username
secret_manager_password = var.secret_manager_password
secrets_path = "service-a/postgres"
secret_data = module.postgres-flex.postgres_credentials["user-a"]
}
module "postgres-credentials-sm-b" {
source = "git::https://commerce-platform.git.onstackit.cloud/commerce-platform-public/terraform-modules//create-secret?ref=main"
secret_manager_instance_id = local.secret_manager_instance_id
secret_manager_username = var.secret_manager_username
secret_manager_password = var.secret_manager_password
secrets_path = "service-b/postgres"
secret_data = module.postgres-flex.postgres_credentials["user-b"]
}
```

View file

@ -10,11 +10,6 @@ variable "postgres_instance_name" {
type = string
}
# variable "postegres_instance_id" {
# description = "postgres instance id"
# type = string
# }
variable "postgres_instance_replicas" {
description = "number of replicas for postgres instance"
type = number
@ -58,19 +53,12 @@ variable "postgres_instance_region" {
type = string
}
# Postgres User Configs
variable "postgres_db_user_name" {
description = "username and owner for postgres db"
type = string
}
variable "postgres_db_user_roles" {
description = "List of database access levels for the user. Supported values are: login, createdb."
type = list(string)
}
# Postgres Database Configs
variable "postgres_db_name" {
description = "db name inside the instance"
type = string
# Postgres User and DB Configs
variable "postgres_databases" {
description = "list of users and databases"
type = list(object({
db_name = string # db name inside the instance
user_name = string # username and owner for postgres db
user_roles = list(string) # List of database access levels for the user. Supported values are: login, createdb.
}))
}

View file

@ -2,7 +2,7 @@ terraform {
required_providers {
stackit = {
source = "stackitcloud/stackit"
version = "~> 0.50.0"
version = "~> 0.68.0"
}
}
}

View file

@ -2,7 +2,7 @@ terraform {
required_providers {
stackit = {
source = "stackitcloud/stackit"
version = "~> 0.50.0"
version = "~> 0.68.0"
}
}

59
service-account/README.md Normal file
View file

@ -0,0 +1,59 @@
# Terraform Module: STACKIT Service Account
This module is designed to create a STACKIT service account, optionally generate a key, and optionally attach it to a server. It is useful for managing service accounts and their associated keys in a secure and repeatable manner.
The purpose of this module is to simplify the creation and management of service accounts in STACKIT, while providing flexibility to generate keys and attach them to servers. It also allows for secure storage of keys using a secrets manager.
## Example Usage
```terraform
module "service-account" {
source = "./service-account" # Or a Git URL "git::https://commerce-platform.git.onstackit.cloud/commerce-platform-public//terraform-modules/service-account"
stackit_project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
service_account_name = "my-service-account"
service_account_create_key = true
}
# Save json created to secrets manager
variable "secret_manager_username" {
description = "username of the secrets manger to store credentials"
type = string
sensitive = true
}
variable "secret_manager_password" {
description = "password of the secrets manger to store credentials"
type = string
sensitive = true
}
module "service_account_key" {
source = "./create-secret" # Or a Git URL "git::https://commerce-platform.git.onstackit.cloud/commerce-platform-public//terraform-modules/create-secret"
secret_manager_instance_id = local.secret_manager_instance_id
secret_manager_username = var.secret_manager_username
secret_manager_password = var.secret_manager_password
secrets_path = "service-accounts/${module.service-account.service_account_name}"
secret_data = {
key_json = module.service-account.service_account_key_json
}
}
```
## Inputs
| Key | Description | Type | Required | Default |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | -------- | ------- |
| `service_account_name` | Name of the service account | `string` | yes |
| `service_account_create_key` | Whether to create a service account key | `bool` | no | `false` |
| `service_account_public_key` | Optional: Specifies the public_key (RSA2048 key-pair). If not provided, a certificate from STACKIT will be used to generate a private_key. | `string` | no | `null` |
| `service_account_rotate_when_changed` | Map to force key rotation when changed | `map(string)` | no | `{}` |
| `service_account_ttl_days` | Key validity duration in days. Defaults to 90 | `number` | no | `90` |
| `attach_to_server` | Whether to attach the service account to a server | `bool` | no | `false` |
| `server_id` | Server ID for attachment | `string` | no | `""` |
## Notes
- When creating a key, it is recommended to save it securely using a secrets manager. In the example usage we illustrated how to do that using the `create-secret` module.
- The module does not handle key rotation automatically. You can use the service_account_rotate_when_changed input to force key rotation when certain attributes change.
- The module does not handle server attachment automatically. You can use the attach_to_server and server_id inputs to attach the service account to a server.
- The module does not handle deletion of service accounts or keys. It is recommended to manage these resources using appropriate Terraform lifecycle configurations or external tools.

View file

@ -0,0 +1,25 @@
output "service_account_name" {
description = "The name of the service account"
value = var.service_account_name
}
output "service_account_email" {
description = "The email of the service account"
value = stackit_service_account.this.email
}
output "service_account_id" {
description = "Internal ID of the service account"
value = stackit_service_account.this.id
}
output "service_account_key_id" {
description = "ID of the created key"
value = try(stackit_service_account_key.this[0].key_id, null)
}
output "service_account_key_json" {
description = "Sensitive JSON key output"
value = try(stackit_service_account_key.this[0].json, null)
sensitive = true
}

View file

@ -0,0 +1,8 @@
terraform {
required_providers {
stackit = {
source = "stackitcloud/stackit"
version = "~> 0.59.0"
}
}
}

View file

@ -0,0 +1,26 @@
resource "stackit_service_account" "this" {
name = var.service_account_name
project_id = var.stackit_project_id
}
resource "time_rotating" "this" {
rotation_days = 3
}
resource "stackit_service_account_key" "this" {
count = var.service_account_create_key ? 1 : 0
project_id = var.stackit_project_id
service_account_email = stackit_service_account.this.email
public_key = var.service_account_public_key
rotate_when_changed = var.service_account_rotate_when_changed
ttl_days = var.service_account_ttl_days
}
resource "stackit_server_service_account_attach" "this" {
count = var.attach_to_server ? 1 : 0
project_id = var.stackit_project_id
server_id = var.server_id
service_account_email = stackit_service_account.this.email
}

View file

@ -0,0 +1,52 @@
variable "stackit_project_id" {
description = "STACKIT project ID"
type = string
}
# === Service Account variables ===
variable "service_account_name" {
description = "Name of the service account"
type = string
}
# === Service Account Key variables ===
variable "service_account_create_key" {
description = "Whether to create a service account key"
type = bool
default = false
}
variable "service_account_public_key" {
description = "Optional: Specifies the public_key (RSA2048 key-pair). If not provided, a certificate from STACKIT will be used to generate a private_key."
type = string
default = null
}
variable "service_account_rotate_when_changed" {
description = "Map to force key rotation when changed"
type = map(string)
default = {}
}
variable "service_account_ttl_days" {
description = "Key validity duration in days. Defaults to 90"
type = number
default = 90
}
# === Server Service Account Attach variables ===
variable "attach_to_server" {
description = "Whether to attach the service account to a server"
type = bool
default = false
}
variable "server_id" {
description = "Server ID for attachment"
type = string
default = ""
}

View file

@ -9,12 +9,14 @@ module "ske-cluster" {
source = "git::https://stackit-hackathon-2025.git.qa.onstackit.cloud/commerce-platform/hackdays-common-infra-poc//terraform/modules/ske-cluster"
stackit_project_id = local.stackit_project_id
ske_cluster_name = "example-cluster"
ske_k8s_version_min = "1.32.7"
ske_node_pools = [
{
name = "example-pool"
machine_type = "c1.2"
minimum = "2"
maximum = "3"
os_version_min = "4230.2.0"
availability_zones = ["eu01-3"]
}
]

View file

@ -2,7 +2,7 @@ terraform {
required_providers {
stackit = {
source = "stackitcloud/stackit"
version = "~> 0.50.0"
version = "~> 0.68.0"
}
}
}

View file

@ -4,13 +4,7 @@ resource "stackit_ske_cluster" "this" {
name = var.ske_cluster_name
maintenance = var.ske_maintenance
node_pools = var.ske_node_pools
#]
#extensions = {
# argus = {
# enabled = true
# argus_instance_id = var.observability-instance-id
# }
#}
kubernetes_version_min = var.ske_k8s_version_min
}
// Kubeconfig

View file

@ -15,6 +15,7 @@ variable "ske_node_pools" {
machine_type = string
minimum = number
maximum = number
os_version_min = string
availability_zones = list(string)
}))
}
@ -29,8 +30,7 @@ variable "ske_maintenance" {
})
}
#variable "observability-instance-id" {
# description = "instance id of the observability instance for cluster monitoring"
# type = string
#
#}
variable "ske_k8s_version_min" {
description = "minimum Kubernetes version"
type = string
}