Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🔴 F5 BIG-IP LTM HTTP/2 Profile Terraform Module

terraform-bigip-ltm-profile-http2 manages a single bigip_ltm_profile_http2 object against a BIG-IP running TMOS >= v12.1.1, via the F5Networks/bigip Terraform provider ~> 1.28.

Terraform Provider Module Type Resources Posture


🧩 Overview

  • 🔷 Manages one bigip_ltm_profile_http2 keystone resource (this) — the LTM HTTP/2 profile attached to a virtual server's profile_names list.
  • 🔷 Exposes every argument the live provider schema supports for this resource — concurrency (concurrent_streams_per_connection), timing (connection_idle_timeout, frame_size, receive_window, write_size, header_table_size), diagnostics (insert_header/insert_header_name, include_content_length), TLS enforcement posture (enforce_tls_requirements), and negotiation control (activation_modes).
  • 🔷 No nested blocks, no child collections — the resource's own schema is flat, so main.tf is a direct 1:1 argument passthrough with no dynamic blocks or for_each.
  • 🔷 Consumed by full-path name from composite modules — most commonly terraform-bigip-ltm-virtual-server — never owned or duplicated by them.

💡 Why it matters: HTTP/2 profile tuning directly affects how many concurrent streams a client can multiplex over one connection and how long BIG-IP keeps that connection open. Getting concurrent_streams_per_connection or connection_idle_timeout wrong is a common source of client-visible stalls under load — modeling this as its own versioned, typed module means the same vetted profile can be reused across every virtual server that needs it, instead of being hand-copied per application.


❤️ 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

graph LR
 VS["terraform-bigip-ltm-virtual-server (keystone target)"]:::target
 HTTP2["terraform-bigip-ltm-profile-http2 (this module)"]:::this
 HTTP["terraform-bigip-ltm-profile-http"]:::sibling
 FASTHTTP["terraform-bigip-ltm-profile-fasthttp"]:::sibling
 HTTPCOMP["terraform-bigip-ltm-profile-httpcompress"]:::sibling
 ONECONNECT["terraform-bigip-ltm-profile-oneconnect"]:::sibling
 TCP["terraform-bigip-ltm-profile-tcp"]:::sibling
 CSSL["terraform-bigip-ltm-profile-client-ssl"]:::sibling
 SSSL["terraform-bigip-ltm-profile-server-ssl"]:::sibling
 POOL["terraform-bigip-ltm-pool"]:::sibling
 COMMON["BIG-IP built-in /Common/http2 profile"]:::external

 COMMON -->|"defaults_from (optional parent)"| HTTP2
 HTTP2 -->|"profile_names (full-path name)"| VS
 HTTP -->|"profile_names (full-path name)"| VS
 FASTHTTP -->|"profile_names (full-path name)"| VS
 HTTPCOMP -->|"profile_names (full-path name)"| VS
 ONECONNECT -->|"profile_names (full-path name)"| VS
 TCP -->|"profile_names (full-path name)"| VS
 CSSL -->|"profile_names (full-path name)"| VS
 SSSL -->|"profile_names (full-path name)"| VS
 POOL -->|"pool_name (full-path name)"| VS

 classDef this fill:#E4002B,color:#ffffff,stroke:#333,stroke-width:1px
 classDef target fill:#000000,color:#ffffff,stroke:#333,stroke-width:1px
 classDef sibling fill:#E8E8E8,color:#000000,stroke:#999,stroke-width:1px
 classDef external fill:#CCCCCC,color:#000000,stroke:#999,stroke-width:1px,stroke-dasharray: 4 2
Loading

This module owns only the HTTP/2 profile itself. terraform-bigip-ltm-virtual-server is the consuming keystone/target — it references this module's name output by full path in its own profile_names list, alongside sibling profile families and terraform-bigip-ltm-pool. The optional defaults_from parent is either BIG-IP's built-in /Common/http2 profile or another instance of this same module.


🧬 What this builds

graph LR
 subgraph Inputs["Inputs (variables.tf)"]
 N["name (required, full path)"]
 DF["defaults_from"]
 CSPC["concurrent_streams_per_connection"]
 CIT["connection_idle_timeout"]
 IH["insert_header / insert_header_name"]
 HTS["header_table_size"]
 ETR["enforce_tls_requirements"]
 FS["frame_size"]
 RW["receive_window"]
 WS["write_size"]
 AM["activation_modes"]
 ICL["include_content_length"]
 end

 R["bigip_ltm_profile_http2.this (keystone)"]:::keystone

 N --> R
 DF --> R
 CSPC --> R
 CIT --> R
 IH --> R
 HTS --> R
 ETR --> R
 FS --> R
 RW --> R
 WS --> R
 AM --> R
 ICL --> R

 R -->|"name (full path)"| O1["output: name"]:::output
 R -->|"id"| O2["output: id"]:::output

 classDef keystone fill:#000000,color:#ffffff,stroke:#333,stroke-width:1px
 classDef output fill:#2E7D32,color:#ffffff,stroke:#333,stroke-width:1px
Loading

Resource inventory

Resource Address Cardinality
bigip_ltm_profile_http2 bigip_ltm_profile_http2.this 1 (keystone; no for_each children — the schema has no nested blocks)

✅ Provider / Versions

Item Value
Terraform >= 1.12.0
Provider F5Networks/bigip ~> 1.28 — re-verify the current minor against the Terraform Registry before each new module wave
Provider block None — this module assumes a single already-authenticated provider "bigip" {} instance is in scope; it never declares address/username/password/token_value

Schema notes that bite

  • name is the full path (/Partition/name) and is the effective primary key. BIG-IP does not support an in-place rename of an LTM object's full path — changing it is a destroy/recreate, not an update.
  • activation_modes is optional and computed in the live schema. Leaving it null (the module default) defers entirely to the device's own computed value; explicitly passing an empty set is a different declaration than null and may not behave the same as leaving it unset.
  • enforce_tls_requirements defaults to "enabled" in both the upstream provider and this module — this mirrors this module suite's secure-by-default posture; there is no invented override here, this is the provider's own default surfaced as-is.
  • This resource's schema is entirely flat — no nested blocks. Unlike terraform-bigip-ltm-profile-client-ssl (which carries cert/key chain references), there is nothing here for main.tf to express with dynamic blocks.

🔑 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. (Source: SCOPE.md.)


F5 BIG-IP Prerequisites

  • iControl REST enabled and reachable on the target device.
  • TMOS >= v12.1.1 (provider floor).
  • Target partition must already exist — this module never creates a partition.

(Source: SCOPE.md.)


📁 Module Structure

terraform-bigip-ltm-profile-http2/
├── providers.tf # required_version, pinned F5Networks/bigip requirement -- no provider {} block
├── variables.tf # flat argument list mirroring bigip_ltm_profile_http2's own flat schema
├── main.tf # single keystone resource, bigip_ltm_profile_http2.this
├── outputs.tf # name (full path) first, then id
├── SCOPE.md # cross-module contract: consumes/emits, role, prerequisites, gotchas
└── README.md # this file

⚙️ Quick Start

# Caller's root module configures the provider -- this module never does.
provider "bigip" {
  address  = var.bigip_address
  username = var.bigip_username
  password = var.bigip_password
}

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

  name = "/Common/app-http2"
}

ℹ️ If the target BIG-IP is deployed single-NIC in a public cloud (AWS/Azure/GCP), the management interface is frequently reachable only on a non-443 port. Set port (env BIGIP_PORT) on the provider block in the root module — connection refused on 443 is the most common first-run failure against a cloud-deployed device.


🔌 Cross-Module Contract

Consumes

Input Type Source module
defaults_from string (full path) Another terraform-bigip-ltm-profile-http2 instance, or BIG-IP's built-in /Common/http2

Emits

Output Description Consumed by
name Full-path name (/Partition/name) of the HTTP/2 profile — the practical cross-reference key terraform-bigip-ltm-virtual-server and other sibling composite modules, via profile_names
id Provider-internal id of the HTTP/2 profile Rarely consumed directly; diagnostics only

📚 Example Library

1 · Minimal, defaults-only profile
module "http2_default" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-http2.git?ref=v1.0.0"

  name = "/Common/app-http2-default"
}

💡 Every argument besides name falls back to the provider's own computed default — concurrent_streams_per_connection = 10, connection_idle_timeout = 300, header_table_size = 4096, frame_size = 2048, receive_window = 32, write_size = 16384.

2 · Higher stream fan-out for a dashboard app
module "http2_dashboard" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-http2.git?ref=v1.0.0"

  name                              = "/Common/dashboard-http2"
  concurrent_streams_per_connection = 128
}

ℹ️ Dashboards that open many parallel fetch/XHR calls over a single HTTP/2 connection benefit from a higher stream ceiling than the provider default of 10.

3 · Longer idle timeout for long-poll / SSE clients
module "http2_longpoll" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-http2.git?ref=v1.0.0"

  name                    = "/Common/longpoll-http2"
  connection_idle_timeout = 1800
}

💡 connection_idle_timeout is in seconds. Applications that hold a connection open for server-sent events or long-polling need this raised well above the 300-second default, or BIG-IP will tear down the connection out from under the client.

4 · Diagnostic header enabled with a custom header name
module "http2_diagnostic_header" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-http2.git?ref=v1.0.0"

  name               = "/Common/diag-http2"
  insert_header      = "enabled"
  insert_header_name = "X-Received-Over-HTTP2"
}

ℹ️ insert_header_name only has an observable effect once insert_header = "enabled" — setting the header name alone (leaving insert_header at its "disabled" default) does not add the header.

5 · Larger header table for header-heavy APIs
module "http2_large_headers" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-http2.git?ref=v1.0.0"

  name              = "/Common/api-http2"
  header_table_size = 8192
}

💡 Raise header_table_size (KB) when upstream clients send large cookie sets or many custom headers that would otherwise thrash HPACK's compression table.

6 · Explicit enforce_tls_requirements = "disabled" (lab-only)
module "http2_lab_no_tls_enforcement" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-http2.git?ref=v1.0.0"

  name                     = "/Common/lab-http2-no-tls-enforce"
  enforce_tls_requirements = "disabled"
}

⚠️ enforce_tls_requirements defaults to "enabled" for a reason — disabling it is the module's one explicit opt-out from this module suite's secure-by-default posture. Reserve this for isolated lab/test rigs, never a member-facing production listener, and route the change through Security/Risk review per this org's regulatory posture before applying it anywhere real.

7 · Smaller frame_size for constrained-MTU links
module "http2_small_frames" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-http2.git?ref=v1.0.0"

  name       = "/Common/satellite-http2"
  frame_size = 1024
}

💡 Smaller frame_size (bytes) can reduce fragmentation on paths with a constrained MTU (e.g. satellite or low-bandwidth WAN backhaul) at the cost of more per-frame overhead.

8 · Larger receive_window for bulk-upload workloads
module "http2_bulk_upload" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-http2.git?ref=v1.0.0"

  name           = "/Common/upload-http2"
  receive_window = 128
}

ℹ️ receive_window (KB) is the flow-control size for upload streams — raise it for applications that push large request bodies (bulk document/file upload endpoints).

9 · Larger write_size for bulk-download workloads
module "http2_bulk_download" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-http2.git?ref=v1.0.0"

  name       = "/Common/download-http2"
  write_size = 65536
}

💡 write_size (bytes) is the combined size of data frames sent in a single write — raising it can improve throughput for large-response/bulk-download endpoints.

10 · activation_modes = ["alpn"] — require negotiated ALPN
module "http2_alpn_only" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-http2.git?ref=v1.0.0"

  name             = "/Common/alpn-only-http2"
  activation_modes = ["alpn"]
}

🔒 Restricting to ["alpn"] means BIG-IP only treats a connection as HTTP/2 when the client negotiated it via TLS ALPN — it will not upgrade a plaintext or non-negotiated connection.

11 · activation_modes = ["always"] — trusted internal mesh listener
module "http2_internal_mesh" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-http2.git?ref=v1.0.0"

  name             = "/Common/internal-mesh-http2"
  activation_modes = ["always"]
}

⚠️ ["always"] handles every incoming connection as HTTP/2 regardless of negotiation — reserve this for a controlled internal service-mesh listener where every client is known to speak HTTP/2, not a general-purpose internet-facing virtual server.

12 · include_content_length = "enabled" for compliance logging
module "http2_content_length_logged" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-http2.git?ref=v1.0.0"

  name                   = "/Common/logged-http2"
  include_content_length = "enabled"
}

ℹ️ Some downstream logging/WAF tooling expects an explicit content-length on HTTP/2 requests; enabling this surfaces it where the default "disabled" would otherwise omit it.

13 · Chaining defaults_from a custom parent profile
module "http2_parent" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-http2.git?ref=v1.0.0"

  name                     = "/Common/org-baseline-http2"
  enforce_tls_requirements = "enabled"
  header_table_size        = 8192
}

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

  name          = "/Common/app-http2-child"
  defaults_from = module.http2_parent.name
}

💡 defaults_from takes the full path of another HTTP/2 profile to inherit from — either BIG-IP's built-in /Common/http2, or (as here) another instance of this same module, letting an organization-wide baseline profile cascade its settings to per-application children.

14 · Non-/Common partition profile
module "http2_tenant_a" {
  source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-http2.git?ref=v1.0.0"

  name = "/Tenant-A/app-http2"
}

🔒 name's full path encodes the partition directly. There is no separate partition variable on this module — the target partition (Tenant-A here) must already exist on the device before apply.

15 · 🏗️ End-to-end composition
provider "bigip" {
  address  = var.bigip_address
  username = var.bigip_username
  password = var.bigip_password
}

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

  name = "/Common/app-http-monitor"
  #... monitor-specific arguments
}

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

  name     = "/Common/app-pool"
  monitors = [module.pool_monitor.name]
  #... members map, load-balancing mode, etc.
}

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

  name                              = "/Common/app-http2"
  concurrent_streams_per_connection = 64
  connection_idle_timeout           = 600
}

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

  name = "/Common/app-clientssl"
  #... certificate/key references, out-of-band provisioned
}

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

  name        = "/Common/app-vs"
  destination = "10.20.30.40:443"
  pool_name   = module.app_pool.name
  profile_names = [
    module.http2_profile.name,
    module.client_ssl_profile.name,
  ]
}

💡 Ordering matters here: the monitor is created before the pool that consumes it, the pool and both profiles before the virtual server that references all three by full-path name. This module never creates the pool, monitor, SSL profile, or virtual server itself — it emits only name/id for its own HTTP/2 profile and lets the composing root module (or a dedicated composite) wire the family together.


📥 Inputs

Grouped summary

Group Variables
Identity name (required), defaults_from
Concurrency & framing concurrent_streams_per_connection, connection_idle_timeout, frame_size, receive_window, write_size, header_table_size
Diagnostics / headers insert_header, insert_header_name, include_content_length
TLS & negotiation enforce_tls_requirements, activation_modes
Full variable reference
Name Type Default Required Description
name string Yes Full path (/Partition/name) of the HTTP/2 profile — the practical primary key used in cross-references.
defaults_from string null No Full path of the parent HTTP/2 profile to inherit from (e.g. /Common/http2). null defers to the device's own computed default.
concurrent_streams_per_connection number 10 No Number of concurrent requests allowed outstanding on a single HTTP/2 connection.
connection_idle_timeout number 300 No Seconds an HTTP/2 connection is left idle before it is eligible for deletion.
insert_header string (enabled|disabled) "disabled" No Whether BIG-IP adds a header indicating the request was received over HTTP/2.
insert_header_name string "X-HTTP2" No Name of the header added when insert_header = "enabled".
header_table_size number 4096 No Size (KB) of the header table used to compress HTTP headers under HTTP/2.
enforce_tls_requirements string (enabled|disabled) "enabled" No Enable/disable enforcement of TLS requirements for this profile. secure default is "enabled".
frame_size number 2048 No Size (bytes) of the data frames sent to the client.
receive_window number 32 No Flow-control size (KB) for upload streams.
write_size number 16384 No Total size (bytes) of combined data frames sent in a single write.
activation_modes set(string) (alpn, always) null No Condition(s) causing BIG-IP to treat an incoming connection as HTTP/2. null defers to the device's optional+computed default.
include_content_length string (enabled|disabled) "disabled" No Whether to include content-length in HTTP/2 headers.

🧾 Outputs

Output Description Sensitive / Conditional
name Full-path name (/Partition/name) of the HTTP/2 profile — the practical cross-reference key No
id Provider-internal id of the HTTP/2 profile No

🧠 Architecture Notes

  • Full-path identity. name is both the argument and the practical primary key. Renaming it is a destroy/recreate from BIG-IP's perspective — there is no in-place rename of an LTM object's full path.
  • Ordering. This profile must exist before any virtual server (or composite module) that references it in a profile_names list — a straightforward "referenced object before referencing object" constraint, enforced implicitly by Terraform's own dependency graph once the consuming module references module.http2_profile.name.
  • activation_modes null-vs-set semantics. Because this argument is optional+computed in the live schema, leaving it null (the module default) is not equivalent to explicitly passing an empty set — null defers entirely to the device's computed value.
  • insert_header/insert_header_name pairing. The two arguments are independent in the schema, but insert_header_name has no observable effect unless insert_header = "enabled" — this module does not enforce that pairing in code (there is no cross-field validation block), so it is called out here rather than silently assumed.
  • No dynamic blocks. main.tf is a pure 1:1 argument passthrough because bigip_ltm_profile_http2's live schema has no nested blocks — this is the simplest possible renderer in the catalog's LTM — Profiles family.

🧱 Design Principles

Concern Secure default Opt-out (caller must type extra)
TLS requirement enforcement enforce_tls_requirements = "enabled" — matches the provider's own default and this module suite's secure-by-default posture Caller explicitly sets "disabled" (see Example 6; lab/test use only)
Partition scope No invented default partition — name's full path always makes the target partition explicit Caller sets a non-/Common path explicitly (see Example 14)

Other rows in this module suite's house-wide secure-by-default table (virtual server destination, persistence/connection limits, AS3/DO declarations, monitor/health-check) do not apply to this resource family — an HTTP/2 profile has no destination, connection limit, or monitor argument of its own.


🚀 Runbook

terraform init -backend=false
terraform validate
terraform fmt -check

Pin every consumption to a tagged release:

source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-http2.git?ref=v1.0.0"

🧪 Testing

Offline proof gate (this repo / CI — no device required)

  • terraform validate catches type mismatches, missing required arguments, and the module's own validation blocks (insert_header, enforce_tls_requirements, include_content_length enum checks; activation_modes closed-set check).
  • terraform fmt -check catches formatting drift.

What only terraform plan/apply against a real device exercises

  • Whether the defaults_from full path actually resolves to an existing profile on that specific device/partition.
  • Whether the target partition named in name actually exists.
  • Actual device-side behavior of activation_modes left null vs. explicitly set (the optional+computed distinction described in Architecture Notes is a live-schema behavior this repo's offline gate cannot observe).
  • Whether header_table_size/frame_size/receive_window/write_size values are accepted by the specific TMOS version in use — the provider does not enforce device-side numeric ranges at plan time.

💬 Example Output

$ terraform apply

module.http2_profile.bigip_ltm_profile_http2.this: Creating...
module.http2_profile.bigip_ltm_profile_http2.this: Creation complete after 1s [id=/Common/app-http2]

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

Outputs:

id = "/Common/app-http2"
name = "/Common/app-http2"

🔍 Troubleshooting

Symptom Cause Fix
insert_header_name set but header never appears on the wire insert_header left at its "disabled" default Set insert_header = "enabled" alongside insert_header_name (see Example 4)
Error: profile already exists on first apply A profile already exists on the device at that full path, created outside Terraform terraform import bigip_ltm_profile_http2.this /Partition/name, or choose a different name
connection refused calling the BIG-IP management API on port 443 Single-NIC cloud-deployed BIG-IP with the management API on a non-443 port Set port (env BIGIP_PORT) on the root module's provider "bigip" {} block
defaults_from reference fails to resolve Parent profile not yet created, or full path/partition typo Confirm the parent profile module applies first, and that the path matches exactly (including partition)
Virtual server reports the profile "not found" when wiring profile_names Partition mismatch between this profile's name and the virtual server's expected partition context Align both objects to the same partition, or confirm cross-partition reference is actually supported for the object types involved

🔗 Related Docs

  • F5Networks/bigip provider docs — bigip_ltm_profile_http2 resource (Terraform Registry).
  • clouddocs.f5.com — LTM HTTP/2 profile concepts and TMOS behavior.
  • Sibling modules: terraform-bigip-ltm-virtual-server, terraform-bigip-ltm-profile-http, terraform-bigip-ltm-profile-client-ssl, terraform-bigip-ltm-pool, terraform-bigip-ltm-monitor.
  • This module's SCOPE.md (cross-module contract, role/prerequisites, provider gotchas).

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

Releases

Packages

Contributors

Languages