Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🔴 F5 BIG-IP LTM SSL Persistence Profile Terraform Module

Manages a single bigip_ltm_persistence_profile_ssl — an SSL-session-ID-based persistence profile — against the F5Networks/bigip Terraform provider ~> 1.28, TMOS >= v12.1.1.

Terraform Provider Module Version Module Type Resources


🧩 Overview

  • 🔐 Manages one bigip_ltm_persistence_profile_ssl.this — a persistence mode that ties a client's subsequent connections to the same pool member based on the SSL session ID negotiated during the handshake, rather than source address or a cookie.
  • 📐 A thin, total, one-to-one passthrough renderer — every provider argument on this resource is a flat scalar, so main.tf has no for_each, no dynamic blocks, and no child collections.
  • 🔗 Emits name (the practical full-path cross-reference key) and id, consumed by terraform-bigip-ltm-virtual-server's persistence_profile_name input — this module never creates or owns a virtual server.
  • 🧬 Requires an explicit defaults_from parent (typically the built-in /Common/ssl template, or a chained instance of this same module) — BIG-IP requires every persistence profile to declare a parent.
  • 🚫 Leaves five Optional+Computed fields (match_across_pools, match_across_services, match_across_virtuals, mirror, override_conn_limit) unset (null) by default so the device's own server-side default applies, instead of this module inventing one.

💡 Why it matters: SSL persistence keeps a re-negotiated or re-encrypted session pinned to the same backend without relying on a cookie the client might not honor or a source address that might be shared behind NAT. Modeling it as an independently versioned, independently tested Terraform object — shared across many virtual servers by full-path name — keeps that decision out of any one virtual server's inline configuration.


❤️ Support this project

If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:

Whether it's a star, a professional connection, or a coffee, every gesture helps keep these modules actively maintained and continually improving. Thank you for being part of the community!


🗺️ Where this fits

flowchart LR
 BUILTIN["/Common/ssl (built-in template)"]:::sibling
 SSLPERSIST["terraform-bigip-ltm-persistence-profile-ssl"]:::this
 VS["terraform-bigip-ltm-virtual-server"]:::keystone
 POOL["terraform-bigip-ltm-pool"]:::sibling
 SRCADDR["terraform-bigip-ltm-persistence-profile-srcaddr"]:::sibling
 DSTADDR["terraform-bigip-ltm-persistence-profile-dstaddr"]:::sibling
 COOKIE["terraform-bigip-ltm-persistence-profile-cookie"]:::sibling

 BUILTIN -- "defaults_from (full path)" --> SSLPERSIST
 SSLPERSIST -- "name (persistence_profile_name)" --> VS
 POOL -- "name (pool_name)" --> VS
 SSLPERSIST -.-> SRCADDR
 SSLPERSIST -.-> DSTADDR
 SSLPERSIST -.-> COOKIE

 classDef this fill:#E4002B,color:#ffffff,stroke:#7a0016,stroke-width:1px;
 classDef keystone fill:#000000,color:#ffffff,stroke:#000000,stroke-width:1px;
 classDef sibling fill:#D9D9D9,color:#000000,stroke:#999999,stroke-width:1px;
Loading

This module (red) is consumed by full-path name from terraform-bigip-ltm-virtual-server (black, the keystone target that assigns the persistence profile to a virtual server alongside its pool). It requires an explicit defaults_from parent, typically the built-in /Common/ssl template. The dotted edges to terraform-bigip-ltm-persistence-profile-srcaddr, -dstaddr, and -cookie mark sibling module families that configure the same BIG-IP concept (persistence) via a different matching strategy — they share no Terraform-level cross-reference with this module.


🧬 What this builds

flowchart TD
 subgraph "Identity and Inheritance"
 ID["name, defaults_from, app_service"]
 end
 subgraph "Cross-Object Persistence Matching"
 XM["match_across_pools, match_across_services, match_across_virtuals"]
 end
 subgraph "HA and Connection Behavior"
 HA["mirror, override_conn_limit"]
 end
 subgraph "Timing"
 TM["timeout"]
 end

 RES(["bigip_ltm_persistence_profile_ssl.this"]):::keystone

 ID --> RES
 XM --> RES
 HA --> RES
 TM --> RES

 RES --> OUT_NAME["output: name"]
 RES --> OUT_ID["output: id"]

 classDef keystone fill:#000000,color:#ffffff,stroke:#000000,stroke-width:1px;
Loading

Resource inventory: exactly one resource, bigip_ltm_persistence_profile_ssl.this. No child resources, no for_each, no dynamic blocks — every one of the 9 module variables maps to a flat top-level argument on that single resource (see main.tf).


✅ Provider / Versions

Requirement Value
Terraform >= 1.12.0
Provider F5Networks/bigip ~> 1.28 (re-verify against the live Terraform Registry listing before each new module wave — F5 ships frequent minor releases)
Provider block None — the caller's root module configures provider "bigip" {}; this module assumes a single already-authenticated provider instance is in scope
TMOS floor >= v12.1.1

Schema notes that bite:

  • name is full-path identity and is immutable — renaming the profile or moving it to a different partition forces destroy/recreate.
  • defaults_from is required — BIG-IP requires an explicit parent for every persistence profile; there is no provider-side default.
  • timeout's rendered provider doc prose reads "(enabled or disabled) Timeout... in seconds" — this is a documentation defect. The compiled schema confirms timeout is type = number (seconds) and Optional, not Computed. This module trusts the compiled schema over the rendered doc text.
  • match_across_pools, match_across_services, match_across_virtuals, mirror, and override_conn_limit are all Optional+Computed — no Terraform-level default is encoded for any of them; left null by default so the device supplies its own server-side value.
  • No tags block, no timeouts {} block — the compiled schema exposes neither, and BIG-IP persistence profiles carry no tagging concept.

🔑 Required BIG-IP User Role / Partition Access

Manager role scoped to the target partition is sufficient; Administrator is not required for this application-layer LTM object. See SCOPE.md for the full cross-module contract this was derived from.

F5 BIG-IP Prerequisites

  • iControl REST enabled and reachable on the target device.
  • TMOS >= v12.1.1.
  • Target partition already exists (this module does not create partitions).
  • The parent profile named in defaults_from (e.g. /Common/ssl, or a prior instance of this module for chained inheritance) already exists on the target device before this profile is applied.

📁 Module Structure

terraform-bigip-ltm-persistence-profile-ssl/
├── providers.tf # required_version >= 1.12.0, F5Networks/bigip ~> 1.28, no provider {} block
├── variables.tf # 9 flat, 1:1-mapped provider arguments; validation{} on the five enabled/disabled fields
├── main.tf # bigip_ltm_persistence_profile_ssl.this — thin, total, one-to-one passthrough
├── outputs.tf # name (primary cross-reference key), id
├── SCOPE.md # cross-module contract (this module's lightweight SCOPE.md)
└── README.md # this file

⚙️ Quick Start

module "ssl_persistence" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-persistence-profile-ssl.git?ref=v1.0.0"

  name          = "/Common/my-ssl-persistence"
  defaults_from = "/Common/ssl"
}

The caller's root module configures the bigip provider (address/username/password or token_value) — this module never declares its own provider {} block and accepts no auth variables.


🔌 Cross-Module Contract

Consumes:

Input Type Source module
defaults_from string (full path) Built-in /Common/ssl template, or another terraform-bigip-ltm-persistence-profile-ssl instance's name output (chained inheritance)
app_service string (full path) iApp deployment process (out of band — not a sibling Terraform module in this catalog)

Emits:

Output Description Consumed by
name Full-path name of the SSL persistence profile (e.g. /Common/my-ssl-persistence) — the practical cross-reference key terraform-bigip-ltm-virtual-server (persistence_profile_name input)
id Provider-internal id — identical value to name for this resource rarely consumed directly

📚 Example Library

1 · Minimal — bare SSL persistence profile

The smallest real call — inherits every setting from the built-in /Common/ssl template except identity.

module "ssl_persistence_basic" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-persistence-profile-ssl.git?ref=v1.0.0"

  name          = "/Common/ssl-persistence-basic"
  defaults_from = "/Common/ssl"
}

ℹ️ match_across_pools, match_across_services, match_across_virtuals, mirror, override_conn_limit, and timeout are all left null here — the BIG-IP device supplies its own server-side default for each rather than this module inventing one.

2 · Custom timeout tuning
module "ssl_persistence_tuned_timeout" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-persistence-profile-ssl.git?ref=v1.0.0"

  name          = "/Common/ssl-persistence-180s"
  defaults_from = "/Common/ssl"
  timeout       = 180
}

ℹ️ timeout is seconds, type = number in the compiled schema — the rendered provider doc's prose mis-describes it as an "(enabled or disabled)" field; do not confuse it with the enabled/disabled string fields below.

3 · Enable mirroring for an HA pair
module "ssl_persistence_mirrored" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-persistence-profile-ssl.git?ref=v1.0.0"

  name          = "/Common/ssl-persistence-mirrored"
  defaults_from = "/Common/ssl"
  mirror        = "enabled"
}

💡 Mirroring replicates the persistence record to the standby unit so a failover doesn't drop in-progress SSL-persisted sessions. Consider this for HA pairs fronting long-lived sessions.

4 · Explicitly disable mirroring
module "ssl_persistence_no_mirror" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-persistence-profile-ssl.git?ref=v1.0.0"

  name          = "/Common/ssl-persistence-no-mirror"
  defaults_from = "/Common/ssl"
  mirror        = "disabled"
}

ℹ️ Useful when the parent (defaults_from) profile has mirroring enabled and this child profile needs to opt back out explicitly rather than inherit it.

5 · Broaden matching across pools
module "ssl_persistence_cross_pool" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-persistence-profile-ssl.git?ref=v1.0.0"

  name               = "/Common/ssl-persistence-cross-pool"
  defaults_from      = "/Common/ssl"
  match_across_pools = "enabled"
}

🔒 Secure-by-default: this module leaves match_across_pools unset unless the caller opts in explicitly, because enabling it broadens where a persistence record is honored beyond the pool that first created it.

6 · Broaden matching across services
module "ssl_persistence_cross_service" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-persistence-profile-ssl.git?ref=v1.0.0"

  name                  = "/Common/ssl-persistence-cross-service"
  defaults_from         = "/Common/ssl"
  match_across_services = "enabled"
}
7 · Broaden matching across virtual servers
module "ssl_persistence_cross_virtual" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-persistence-profile-ssl.git?ref=v1.0.0"

  name                  = "/Common/ssl-persistence-cross-virtual"
  defaults_from         = "/Common/ssl"
  match_across_virtuals = "enabled"
}
8 · Combined broad matching (pools + services + virtuals)
module "ssl_persistence_broad_match" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-persistence-profile-ssl.git?ref=v1.0.0"

  name                  = "/Common/ssl-persistence-broad"
  defaults_from         = "/Common/ssl"
  match_across_pools    = "enabled"
  match_across_services = "enabled"
  match_across_virtuals = "enabled"
}

⚠️ This is the broadest persistence scope this resource supports — a client's persistence record becomes honored across every pool, service, and virtual server on the device. Treat this as a deliberate architectural decision, not a default posture; each field defaults to null (device default) precisely so this combination is never accidental.

9 · Opt-in override of pool member connection limits
module "ssl_persistence_override_conn_limit" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-persistence-profile-ssl.git?ref=v1.0.0"

  name                = "/Common/ssl-persistence-override-limit"
  defaults_from       = "/Common/ssl"
  override_conn_limit = "enabled"
}

🔒 Secure-by-default: the caller must set override_conn_limit = "enabled" explicitly to accept bypassing pool member connection limits for persisted clients. Per-virtual connection limits remain hard limits and are never overridden regardless of this setting.

10 · Chained custom base profile via defaults_from

First a base profile inheriting directly from the built-in template, then a second profile that chains inheritance from the first instead of /Common/ssl alone.

module "ssl_persistence_base" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-persistence-profile-ssl.git?ref=v1.0.0"

  name          = "/Common/ssl-persistence-base"
  defaults_from = "/Common/ssl"
  timeout       = 300
}

module "ssl_persistence_chained" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-persistence-profile-ssl.git?ref=v1.0.0"

  name          = "/Common/ssl-persistence-chained"
  defaults_from = module.ssl_persistence_base.name
  mirror        = "enabled"
}

ℹ️ defaults_from accepts any existing persistence profile's full path, not only a built-in template — chaining lets a team-wide base profile's settings flow into several more specific child profiles.

11 · iApp-scoped profile via app_service
module "ssl_persistence_iapp" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-persistence-profile-ssl.git?ref=v1.0.0"

  name          = "/Common/ssl-persistence-iapp-scoped"
  defaults_from = "/Common/ssl"
  app_service   = "/Common/my-iapp.app/my-iapp"
}

ℹ️ app_service is only meaningful when this profile is managed as part of an iApp deployment; left null otherwise (the module default).

12 · Short timeout for latency-sensitive persistence
module "ssl_persistence_short_timeout" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-persistence-profile-ssl.git?ref=v1.0.0"

  name          = "/Common/ssl-persistence-short"
  defaults_from = "/Common/ssl"
  timeout       = 30
}

💡 A shorter timeout releases a persistence record sooner after a client goes idle — useful for high-churn, high-concurrency applications where holding a pinned record too long wastes pool capacity.

13 · All fields explicit

Every optional argument set explicitly, rather than relying on device-side Optional+Computed defaults — useful as a reference for the full field set.

module "ssl_persistence_full" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-persistence-profile-ssl.git?ref=v1.0.0"

  name                  = "/Common/ssl-persistence-full"
  defaults_from         = "/Common/ssl"
  match_across_pools    = "disabled"
  match_across_services = "disabled"
  match_across_virtuals = "disabled"
  mirror                = "disabled"
  override_conn_limit   = "disabled"
  timeout               = 300
  app_service           = null
}

ℹ️ Setting every enabled/disabled field to "disabled" here pins the profile's behavior explicitly in version control instead of leaving it to whatever the device's own default happens to be — useful when a change to the device's default should never silently change this profile's behavior.

🏗️ 14 · End-to-end composition

Wires this module's output into terraform-bigip-ltm-virtual-server, alongside a pool from terraform-bigip-ltm-pool — the full chain from SSL persistence profile to a load-balanced virtual server.

module "app_ssl_persistence" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-persistence-profile-ssl.git?ref=v1.0.0"

  name          = "/Common/app-ssl-persistence"
  defaults_from = "/Common/ssl"
  timeout       = 180
  mirror        = "enabled"
}

module "app_pool" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-pool.git?ref=v1.0.0"

  name          = "/Common/app-pool"
  monitor_names = ["/Common/app-http-monitor"]
  #...node/member wiring per terraform-bigip-ltm-pool's own README
}

module "app_virtual_server" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-virtual-server.git?ref=v1.0.0"

  name                     = "/Common/app-vs"
  pool_name                = module.app_pool.name
  persistence_profile_name = module.app_ssl_persistence.name
  destination              = "10.20.30.40:443"
  #...remaining virtual-server wiring per terraform-bigip-ltm-virtual-server's own README
}

💡 This module never creates the virtual server itself — it only emits the full-path name that terraform-bigip-ltm-virtual-server consumes, keeping the persistence profile's lifecycle independent of any one virtual server that happens to reference it.


📥 Inputs

Grouped summary (9 inputs total; only name and defaults_from are required):

Group Variables
Identity & inheritance name (required), defaults_from (required), app_service
Cross-object persistence matching match_across_pools, match_across_services, match_across_virtuals
HA & connection behavior mirror, override_conn_limit
Timing timeout
Full variable reference
Variable Type Default Notes
name string — (required) Full-path profile name. Immutable — rename/repartition forces destroy/recreate.
defaults_from string — (required) Full path of the parent persistence profile (e.g. /Common/ssl). No provider-side default.
match_across_pools string null Closed set: enabled, disabled. Optional+Computed — device default applies when null.
match_across_services string null Closed set: enabled, disabled. Optional+Computed — device default applies when null.
match_across_virtuals string null Closed set: enabled, disabled. Optional+Computed — device default applies when null.
mirror string null Closed set: enabled, disabled. Optional+Computed — device default applies when null.
override_conn_limit string null Closed set: enabled, disabled. Optional+Computed — device default applies when null.
timeout number null Seconds. Compiled schema confirms type = number; rendered provider doc prose is misleading (see Schema notes).
app_service string null Full-path name of the iApp this profile belongs to, if any.

🧾 Outputs

Output Description Sensitive
name Full-path name of the SSL persistence profile (e.g. /Common/my-ssl-persistence) — the practical cross-reference key for sibling modules No
id Provider-internal id — identical value to name for this resource (no separate numeric id) No

🧠 Architecture Notes

  • Flat passthrough, no rendering logic. main.tf maps every one of the 9 variables directly onto bigip_ltm_persistence_profile_ssl.this's matching argument — there is no for_each, no dynamic block, and no try wrapping, because every argument is a top-level scalar on this resource (unlike composite modules such as terraform-bigip-ltm-pool, which render nested member collections).
  • Full-path identity drives replacement, not update. Because name encodes partition inline, changing partition or renaming the profile is a destroy/recreate, not an in-place change — any sibling module (e.g. terraform-bigip-ltm-virtual-server) still holding the old full path will fail to resolve it until its own persistence_profile_name input is updated in lockstep.
  • Five fields are Optional+Computed, not merely Optional. match_across_pools, match_across_services, match_across_virtuals, mirror, and override_conn_limit are left null by default so the device supplies its own server-side default rather than this module guessing one — this also means terraform plan may show a computed value appear after the first apply even though the caller never set it.
  • timeout's type is verified against the compiled schema, not the rendered doc. The registry's rendered prose describes timeout as "(enabled or disabled)," which is incorrect for this argument; variables.tf documents this discrepancy inline.
  • No secret-shaped inputs. Unlike terraform-bigip-ltm-monitor (which carries a password), this resource's compiled schema exposes no credential-shaped argument, so no variable in this module is marked sensitive = true.

🧱 Design Principles

Concern This module's default Opt-out (caller must type extra)
Partition scope No invented default — name must carry the full path (partition + name) explicitly; this module never assumes /Common Caller always supplies the full path in name
Cross-pool/service/virtual matching null (device default) — this module never silently broadens where a persistence record is honored Caller explicitly sets match_across_pools / match_across_services / match_across_virtuals to "enabled" (see Examples 5–8)
Connection-limit override null (device default) — this module does not silently opt in to bypassing pool member connection limits Caller explicitly sets override_conn_limit = "enabled" (see Example 9)
Mirroring null (device default) Caller explicitly sets mirror = "enabled" or "disabled" (see Examples 3–4)
Parent profile (defaults_from) No default — required input; BIG-IP itself requires an explicit parent N/A — hard rule, not a toggle

⚠️ Security trade-off called out explicitly: broadening match_across_pools, match_across_services, or match_across_virtuals (Examples 5–8) widens where a client's persistence record is honored across the device — beyond the single pool/service/virtual server that first created it. This module never enables that broadening implicitly; route any change to this posture for a production profile through the same Risk/Architecture review that would apply to any other traffic-scoping change, consistent with our regulated-environment posture.


🚀 Runbook

cd C:\GitHubCode\newf5modules\bigip\terraform-bigip-ltm-persistence-profile-ssl
terraform init -backend=false
terraform validate
terraform fmt -check

Pin consumers to a released tag, e.g. ?ref=v1.0.0 — never to an unpinned branch.


🧪 Testing

This is an offline proof gate — it confirms the module is internally consistent, not that a real BIG-IP device accepts the configuration:

Command Proves Does NOT prove
terraform init -backend=false Provider requirement resolves, no backend needed for a child module Reachability of any real BIG-IP device
terraform validate Types, required arguments, and validation{} blocks are internally consistent That defaults_from names a parent profile that actually exists on the target device
terraform fmt -check Canonical HCL formatting Anything about runtime behavior

Only a real terraform plan/apply against an authenticated bigip provider instance exercises whether the target device actually accepts the chosen defaults_from parent, or how the five Optional+Computed fields resolve on that specific device/version — that step is a root-module/pipeline concern, not something this module's own offline gate can cover.


💬 Example Output

$ terraform apply

module.app_ssl_persistence.bigip_ltm_persistence_profile_ssl.this: Creating...
module.app_ssl_persistence.bigip_ltm_persistence_profile_ssl.this: Creation complete after 1s [id=/Common/app-ssl-persistence]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

name = "/Common/app-ssl-persistence"
id = "/Common/app-ssl-persistence"

🔍 Troubleshooting

Symptom Cause Fix
BIG-IP API error referencing an unknown parent profile defaults_from names a profile that doesn't exist on this device Confirm the parent (e.g. /Common/ssl, or a prior instance of this module) exists before applying
Terraform wants to destroy and recreate the profile on an otherwise-small change name (full path) changed — partition or profile name edited Full-path identity is immutable; treat any rename as a deliberate destroy/recreate, and update every sibling module's reference (e.g. terraform-bigip-ltm-virtual-server's persistence_profile_name) in the same change
terraform plan shows a value appear for a field the caller never set match_across_pools/match_across_services/match_across_virtuals/mirror/override_conn_limit are Optional+Computed — the device supplied its own default on first apply Expected behavior; set the field explicitly if a specific value must be pinned in version control (see Example 13)
Persistence record honored more broadly than expected match_across_pools/match_across_services/match_across_virtuals set to "enabled" somewhere in the chain (this profile or its defaults_from parent) Review Examples 5–8 and the Design Principles security trade-off callout; confirm this was a deliberate choice
Virtual server never reports this persistence profile as attached Sibling terraform-bigip-ltm-virtual-server invocation's persistence_profile_name input wasn't updated with this module's name output Wire module.<this>.name into the virtual-server module's persistence_profile_name input

🔗 Related Docs

  • F5 Terraform provider reference: F5Networks/bigip bigip_ltm_persistence_profile_ssl resource, Terraform Registry.
  • F5 clouddocs: persistence-profile role/permission model at clouddocs.f5.com.
  • Sibling modules: terraform-bigip-ltm-virtual-server, terraform-bigip-ltm-pool, terraform-bigip-ltm-persistence-profile-srcaddr, terraform-bigip-ltm-persistence-profile-dstaddr, terraform-bigip-ltm-persistence-profile-cookie.
  • This module's own SCOPE.md (cross-module contract).

💙 "Infrastructure as Code should be standardized, consistent, and secure."

Releases

Packages

Contributors

Languages