Manages a single
bigip_ltm_persistence_profile_ssl— an SSL-session-ID-based persistence profile — against theF5Networks/bigipTerraform provider~> 1.28, TMOS>= v12.1.1.
- 🔐 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.tfhas nofor_each, nodynamicblocks, and no child collections. - 🔗 Emits
name(the practical full-path cross-reference key) andid, consumed byterraform-bigip-ltm-virtual-server'spersistence_profile_nameinput — this module never creates or owns a virtual server. - 🧬 Requires an explicit
defaults_fromparent (typically the built-in/Common/ssltemplate, 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.
If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:
- ⭐ Star this repository to help others discover this Terraform module.
- 🤝 Connect with me on LinkedIn: linkedin.com/in/microsoftexpert
- ☕ Buy me a coffee: buymeacoffee.com/microsoftexpert
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!
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;
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.
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;
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).
| 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:
nameis full-path identity and is immutable — renaming the profile or moving it to a different partition forces destroy/recreate.defaults_fromis 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 confirmstimeoutistype = 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, andoverride_conn_limitare all Optional+Computed — no Terraform-level default is encoded for any of them; leftnullby default so the device supplies its own server-side value.- No
tagsblock, notimeouts {}block — the compiled schema exposes neither, and BIG-IP persistence profiles carry no tagging concept.
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.
- 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.
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
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.
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 |
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, andtimeoutare all leftnullhere — 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
}ℹ️
timeoutis seconds,type = numberin the compiled schema — the rendered provider doc's prose mis-describes it as an "(enabled or disabled)" field; do not confuse it with theenabled/disabledstring 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_poolsunset 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 tonull(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_fromaccepts 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_serviceis only meaningful when this profile is managed as part of an iApp deployment; leftnullotherwise (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/disabledfield 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
namethatterraform-bigip-ltm-virtual-serverconsumes, keeping the persistence profile's lifecycle independent of any one virtual server that happens to reference it.
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. |
| 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 |
- Flat passthrough, no rendering logic.
main.tfmaps every one of the 9 variables directly ontobigip_ltm_persistence_profile_ssl.this's matching argument — there is nofor_each, nodynamicblock, and notrywrapping, because every argument is a top-level scalar on this resource (unlike composite modules such asterraform-bigip-ltm-pool, which render nested member collections). - Full-path identity drives replacement, not update. Because
nameencodes 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 ownpersistence_profile_nameinput is updated in lockstep. - Five fields are Optional+Computed, not merely Optional.
match_across_pools,match_across_services,match_across_virtuals,mirror, andoverride_conn_limitare leftnullby default so the device supplies its own server-side default rather than this module guessing one — this also meansterraform planmay show a computed value appear after the firstapplyeven though the caller never set it. timeout's type is verified against the compiled schema, not the rendered doc. The registry's rendered prose describestimeoutas "(enabled or disabled)," which is incorrect for this argument;variables.tfdocuments this discrepancy inline.- No secret-shaped inputs. Unlike
terraform-bigip-ltm-monitor(which carries apassword), this resource's compiled schema exposes no credential-shaped argument, so no variable in this module is markedsensitive = true.
| 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: broadeningmatch_across_pools,match_across_services, ormatch_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.
cd C:\GitHubCode\newf5modules\bigip\terraform-bigip-ltm-persistence-profile-ssl
terraform init -backend=false
terraform validate
terraform fmt -checkPin consumers to a released tag, e.g. ?ref=v1.0.0 — never to an unpinned branch.
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.
$ 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"
| 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 |
- F5 Terraform provider reference:
F5Networks/bigipbigip_ltm_persistence_profile_sslresource, 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."