STACKIT Terraform Provider

repository·main·Indexed 41 days ago

https://github.com/stackitcloud/terraform-provider-stackit

The STACKIT Terraform Provider allows users to manage STACKIT cloud resources using Terraform HCL. It supports Key and Token authentication flows, S3 and PostgreSQL backend configurations, and provides access to beta resources and experimental features such as IAM, routing tables, and SKE.

Tokens
216.3K
Snippets
545
Records
810
Agent score
51%

What's inside STACKIT

  1. Configure Dremio authentication types

    main

    The authentication block defines how users access the Dremio instance. You must specify a type. Depending on the type, different nested configuration blocks are required:

    • local-only: No additional configuration required.
    • azuread: Requires the azuread block.
    • oauth: Requires the oauth block.

    Note: Changing authentication settings causes a Dremio restart and downtime.

  2. Manage Redis instances with stackit_redis_instance

    main

    The stackit_redis_instance resource allows you to provision and manage Redis instances within STACKIT.

    ⚠️ Deprecation Warning: This resource is deprecated and will be removed after August 2027. Users are encouraged to migrate to stackit_valkey_instance instead. Follow the official migration guide to transition your data.

    Note on Configuration: You must have a region specified in your provider configuration. If you do not define a region in the resource block, the provider's default region will be used.

    resource "stackit_redis_instance" "example" {
      project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
      name       = "example-instance"
      version    = "7"
      plan_name  = "stackit-redis-1.2.10-replica"
      parameters = {
        sgw_acl                 = "193.148.160.0/19,45.129.40.0/21,45.135.244.0/22"
        enable_monitoring       = false
        down_after_milliseconds = 30000
        syslog                  = ["logs4.your-syslog-endpoint.com:54321"]
      }
    }
  3. Generate short-lived STACKIT access tokens with stackit_access_token

    main

    The stackit_access_token is an ephemeral resource that generates a short-lived STACKIT access token (JWT). This token is useful for authenticating with external providers (like restapi) that require a STACKIT Bearer token during a Terraform run.

    Key Behaviors

    • Lifecycle: A new token is generated each time the resource is evaluated, but it remains consistent for the duration of a single Terraform operation.
    • Expiration: Tokens generated from service account keys expire after 60 minutes.
    • Key Extraction: If a private_key is not explicitly provided, the provider attempts to extract it from the service account key file.

    Requirements

    • Authentication: Service account key credentials must be configured either in the STACKIT provider configuration or via environment variables. If any other authentication method is used, this resource will fail.
    • Beta Status: This is a beta resource. To use it, you must set enable_beta_resources = true in your stackit provider configuration.
    provider "stackit" {
      default_region           = "eu01"
      service_account_key_path = "/path/to/sa_key.json"
      enable_beta_resources    = true
    }
    
    ephemeral "stackit_access_token" "example" {}
  4. Configure WAF for CDN distributions

    main

    The config.waf block configures the Web Application Firewall. If the waf block is omitted, the mode defaults to DISABLED and the type to FREE.

    WAF Modes and Tiers

    • mode: ENABLED (blocks threats), LOG_ONLY (logs matches), or DISABLED (turns off inspection).
    • type: FREE or PREMIUM.
    • paranoia_level: Aggression level from L1 to L4 (defaults to L1).

    WAF Precedence Hierarchy

    Rules follow a specific precedence order. Higher precedence settings override lower ones:

    1. Specific Rules (enabled_rule_ids, disabled_rule_ids, log_only_rule_ids) override Groups.
    2. Groups (enabled_rule_group_ids, etc.) override Collections.
    3. Collections (enabled_rule_collection_ids, etc.) are the base level.

    WAF Restrictions

    You can restrict incoming requests using:

    • allowed_http_methods: e.g., GET, POST.
    • allowed_http_versions: e.g., HTTP/2.
    • allowed_request_content_types: e.g., application/json.

    Note: If you remove the waf block from your configuration, the API retains the last known state for existing properties.

  5. Configure alert_config for observability instances

    main

    The alert_config block allows you to define how alerts are received and routed. It consists of three main parts:

    1. Receivers (receivers)

    A list of alert receivers. Each receiver must have a name and can include:

    • email_configs: SMTP settings including auth_identity, auth_password, auth_username, from, send_resolved, smart_host, and to.
    • opsgenie_configs: Settings for OpsGenie including api_key, api_url, priority (P1-P5), send_resolved, and tags.
    • webhooks_configs: HTTP POST endpoints. Use google_chat or ms_teams boolean flags for special handling. Includes url and send_resolved.

    2. Routing (route)

    Defines how alerts are dispatched to receivers.

    • receiver (Required): The name of the receiver to route to.
    • group_by (Optional): Labels to group alerts (e.g., ['cluster', 'alertname']). Use ['...'] to disable aggregation.
    • group_wait (Optional): Time to wait before sending initial notification.
    • group_interval (Optional): Time to wait before sending notifications for new alerts in an existing group.
    • repeat_interval (Optional): Time to wait before re-sending a notification for an existing alert.
    • routes (Optional): A list of child routes for more granular routing. Each child route supports matchers (PromQL/OpenMetrics syntax) and can use continue to allow matching subsequent sibling nodes.

    3. Global Configuration (global)

    Optional global settings for all alerts. Note: You must update the entire global block at once; individual attributes cannot be updated independently.

    • opsgenie_api_key / opsgenie_api_url
    • resolve_timeout: Default time before an alert is declared resolved if not updated.
    • smtp_* settings: smtp_auth_identity, smtp_auth_password, smtp_auth_username, smtp_from, and smtp_smart_host.
  6. Configure a cross-network Load Balancer setup

    main

    When the Load Balancer and the target server reside in different networks, you must manually manage security group assignments. This is an advanced configuration that requires two specific steps:

    1. Set disable_security_group_assignment = true in the stackit_loadbalancer resource. This prevents the provider from automatically managing security groups.
    2. Use the load_balancer_security_group_id attribute from the stackit_loadbalancer resource to create a stackit_security_group_rule on the target server's security group. This allows ingress traffic specifically from the Load Balancer.

    This pattern ensures that even though the resources are isolated in different networks, the Load Balancer can still communicate with the backend targets.

    # 1. Define separate networks
    resource "stackit_network" "lb_network" {
      project_id       = var.project_id
      name             = "lb-network-example"
      ipv4_prefix      = "192.168.10.0/25"
      ipv4_nameservers = ["8.8.8.8"]
      routed           = true
    }
    
    resource "stackit_network" "target_network" {
      project_id       = var.project_id
      name             = "target-network-example"
      ipv4_prefix      = "192.168.15.0/25"
      ipv4_nameservers = ["8.8.8.8"]
    }
    
    # 2. Configure Load Balancer for manual security group mode
    resource "stackit_loadbalancer" "example" {
      project_id       = var.project_id
      name             = "example-advanced-lb"
      external_address = stackit_public_ip.example.ip
      disable_security_group_assignment = true
    
      networks = [{
        network_id = stackit_network.lb_network.network_id
        role       = "ROLE_LISTENERS_AND_TARGETS"
      }]
    
      listeners = [{
        port        = 80
        protocol    = "PROTOCOL_TCP"
        target_pool = "cross-network-pool"
      }]
    
      target_pools = [{
        name        = "cross-network-pool"
        target_port = 80
        targets = [{
          display_name = stackit_server.example.name
          ip           = stackit_network_interface.nic.ipv4
        }]
      }]
    }
    
    # 3. Create security group and rule to allow LB ingress
    resource "stackit_security_group" "target_sg" {
      project_id  = var.project_id
      name        = "target-sg-for-lb-access"
      description = "Allows ingress traffic from the example load balancer."
    }
    
    resource "stackit_security_group_rule" "allow_lb_ingress" {
      project_id        = var.project_id
      security_group_id = stackit_security_group.target_sg.security_group_id
      direction         = "ingress"
      protocol = {
        name = "tcp"
      }
    
      # Link to the LB's security group
      remote_security_group_id = stackit_loadbalancer.example.load_balancer_security_group_id
    
      port_range = {
        min = 80
        max = 80
      }
    }
    
    # 4. Attach target server to the target network and security group
    resource "stackit_server" "example" {
      project_id        = var.project_id
      name              = "example-remote-target"
      machine_type      = "g2i.2"
      availability_zone = "eu01-1"
      boot_volume = {
        source_type = "image"
        source_id   = var.image_id
        size        = 10
      }
      network_interfaces = [
        stackit_network_interface.nic.network_interface_id
      ]
    }
    
    resource "stackit_network_interface" "nic" {
      project_id         = var.project_id
      network_id         = stackit_network.target_network.network_id
      security_group_ids = [stackit_security_group.target_sg.security_group_id]
    }
  7. Configure WAF (Web Application Firewall) in CDN distributions

    main

    The waf configuration block within the config object allows you to secure your distribution.

    Precedence Hierarchy

    Rules follow a strict precedence order. If a conflict occurs, the higher precedence level wins:

    1. Specific Rules (Highest Precedence)
    2. Groups
    3. Collections (Lowest Precedence)

    For example, if you enable a whole collection but explicitly disable a specific Rule ID within that collection, the specific Rule ID setting will take effect.

    WAF Configuration Options

    • mode: Set to "ENABLED" to activate WAF.
    • type: e.g., "PREMIUM".
    • paranoia_level: e.g., "L1".
    • allowed_http_versions: List of versions like ["HTTP/1.0", "HTTP/1.1"].
    • allowed_http_methods: List of methods like ["GET"].
    • allowed_request_content_types: List of content types.

    Rule Management

    • Collections: Use enabled_rule_collection_ids, disabled_rule_collection_ids, and log_only_rule_collection_ids.
    • Groups: Use enabled_rule_group_ids, disabled_rule_group_ids, and log_only_rule_group_ids.
    • Specific Rules: Use enabled_rule_ids, disabled_rule_ids, and log_only_rule_ids for the highest precedence control.

    To find available IDs, consult the Stackit API documentation.

    waf = {
      mode                          = "ENABLED"
      type                          = "PREMIUM"
      paranoia_level                = "L1"
      allowed_http_versions         = ["HTTP/1.0", "HTTP/1.1"]
      allowed_http_methods          = ["GET"]
      allowed_request_content_types = ["text/plain"]
    
      # Collections
      enabled_rule_collection_ids  = ["@builtin/crs/request"]
      disabled_rule_collection_ids = []
      log_only_rule_collection_ids = ["@builtin/crs/response"]
    
      # Groups
      enabled_rule_group_ids  = []
      disabled_rule_group_ids = []
      log_only_rule_group_ids = []
    
      # Specific Rules (Highest Precedence)
      enabled_rule_ids  = ["@builtin/crs/request/913100"]
      disabled_rule_ids = ["@builtin/crs/request/942151"]
      log_only_rule_ids = ["@builtin/crs/response/954120"]
    }
  8. How encrypted volumes work for SKE using Service Account Impersonation

    main
    To enable encrypted volumes in STACKIT Kubernetes Engine (SKE), you must implement a Service Account Impersonation (Act-As) pattern. Because the internal SKE service account needs to perform encryption/decryption tasks, it cannot hold KMS permissions directly for security reasons. Instead, you create a dedicated manager service account that holds the kms.admin role, and then authorize the internal SKE service account to impersonate (Act-As) that manager. This allows the SKE CSI driver to perform KMS operations on behalf of the manager account when provisioning volumes.
  9. Rotate VPN Pre-Shared Keys using `pre_shared_key_wo`

    main

    To rotate the IPsec tunnel pre-shared key, use the write-only argument pre_shared_key_wo instead of pre_shared_key. Because pre_shared_key_wo is write-only and never stored in the state, changing it alone will not trigger a Terraform update.

    To successfully rotate the key, you must:

    1. Update the pre_shared_key_wo value.
    2. Increment the pre_shared_key_wo_version number.

    Note: The key must be a minimum of 20 characters.

  10. Configure Affinity Group policies

    main

    When creating a stackit_affinity_group, you must specify a policy. The available policies are:

    • hard-affinity: All servers launched in this group will be hosted on the same compute node.
    • hard-anti-affinity: All servers launched in this group will be hosted on different compute nodes.
    • soft-affinity: All servers launched in this group will be hosted on the same compute node if possible, but if not possible they still will be scheduled instead of failure.
    • soft-anti-affinity: All servers launched in this group will be hosted on different compute nodes if possible, but if not possible they still will be scheduled instead of failure.
  11. Avoid 404 errors by forcing kubeconfig evaluation during Apply

    main

    Because ephemeral resources evaluate during the Terraform Plan phase if their inputs are known, using a cluster name that hasn't been created yet will result in a 404 error from the API.

    To prevent this, use a ternary operator on the cluster ID to ensure the cluster_name input remains an empty string until the cluster actually exists (during the Apply phase).

    ephemeral "stackit_ske_kubeconfig" "example" {
      project_id   = stackit_ske_cluster.example.project_id
      cluster_name = stackit_ske_cluster.example.id != "" ? stackit_ske_cluster.example.name : ""
    }