terraform-bigip-ltm-policymanages a singlebigip_ltm_policy-- an LTM local traffic policy with itsrule(condition/action) blocks rendered inline, since no separate provider resource exists for a policy rule -- against theF5Networks/bigipTerraform provider~> 1.28, TMOS>= v12.1.1.
- π§ Manages one
bigip_ltm_policy.this-- an LTM local traffic policy that steers requests via orderedruleentries, each a condition/action pair evaluated against the policy's ownstrategy(e.g. first-match, best-match, all-match). - π§΅ Renders
ruleas adynamicblock over amap(object(...))keyed by each rule's own name -- BIG-IP has no separate provider resource for a policy rule, so growing or shrinking the rule set never re-indexes another rule's plan entry (for_each, nevercount). - π Each rule carries its own nested
conditionandactionlists (ordered, ANDed unless a condition'snot/allflags say otherwise) -- both are 1:1 mirrors of the livebigip_ltm_policyprovider schema, field-for-field. - π Consumes pools by full-path name inside
rule[*].action[*].poolfromterraform-bigip-ltm-pool-- this module never creates a pool itself, only references one. - π Deliberately omits
published_copyas a module variable -- it is deprecated on the live resource and the policy auto-publishes; surfacing it would only invite callers to depend on a no-op field. - π·οΈ Emits
name(full path, the practical cross-reference key),id, andrule_names(diagnostics), consumed byterraform-bigip-ltm-virtual-server'spolicy_nameslist. - π« Owns nothing downstream -- virtual servers that attach this policy live in the sibling
terraform-bigip-ltm-virtual-servermodule, which consumes this module'snameoutput by full path.
π‘ Why it matters: a local traffic policy externalizes request-steering logic (URI/host routing, header rewrites, redirects, pool selection) out of iRule TCL and into a condition/action table that Terraform can diff rule-by-rule. Keying each rule on its own name, rather than a list index, means adding rule #6 never touches the plan for rules #1-5.
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
POOL["terraform-bigip-ltm-pool"]
POLICY["terraform-bigip-ltm-policy (this module)"]
VS["terraform-bigip-ltm-virtual-server"]
POOL -->|"pool_name, full-path, referenced inside rule actions"| POLICY
POLICY -->|"name, full-path, consumed as policy_names"| VS
style POLICY fill:#E4002B,color:#ffffff
style VS fill:#000000,color:#ffffff
style POOL fill:#ECECEC,color:#000000
terraform-bigip-ltm-pool is this module's only formally tracked upstream input (per SCOPE.md's
Consumes table) -- a rule's action.pool field takes a pool's full-path name output, and this
module never creates that pool itself. terraform-bigip-ltm-virtual-server is the primary downstream
consumer, wiring this module's name output into its own policy_names list. This module never
creates or references a virtual server (see SCOPE.md "Out of scope / consumed by name").
flowchart TD
subgraph Identity["Identity"]
NAME["var.name"]
DESC["var.description"]
STRAT["var.strategy"]
end
subgraph ProtocolControls["Protocol and Controls"]
CTRL["var.controls"]
REQ["var.requires"]
end
subgraph Rules["Rules, for_each"]
RULE["rule, dynamic block, keyed by rule name"]
COND["condition, dynamic, nested in rule"]
ACT["action, dynamic, nested in rule"]
end
RES(["bigip_ltm_policy.this"]):::keystone
NAME --> RES
DESC --> RES
STRAT --> RES
CTRL --> RES
REQ --> RES
RULE -->|"for_each over var.rule"| RES
COND -->|"nested per rule"| RULE
ACT -->|"nested per rule"| RULE
RES --> OUT_NAME["output: name"]
RES --> OUT_ID["output: id"]
RES --> OUT_RULES["output: rule_names"]
classDef keystone fill:#000000,color:#ffffff,stroke:#000000,stroke-width:1px;
Resource inventory: exactly one resource, bigip_ltm_policy.this. There is no separate child
resource -- rule, and the condition/action blocks nested inside each rule, are all rendered
as dynamic blocks against a single keystone resource, not independent managed resources.
| Resource | Count | Notes |
|---|---|---|
bigip_ltm_policy.this |
1 (single keystone) | The policy object itself -- identity, strategy, protocol/controls, and every rule's condition/action pairs. |
| Requirement | Value |
|---|---|
| Terraform | >= 1.12.0 |
F5Networks/bigip provider |
~> 1.28 (re-verify against the Terraform Registry before each new module wave) |
| Provider block | None -- the caller's root module configures address/username/password/token_value |
| BIG-IP TMOS | >= v12.1.1 (provider floor) |
Schema notes that bite:
nameis the full-path identity (/Partition/name) -- the livebigip_ltm_policyschema exposes onlyname, with no separatepartition/full_pathattribute (unlikebigip_ltm_ifile); this was verified against the liveF5Networks/bigipv1.28.0 provider schema during authoring.published_copyis deliberately not exposed as a module variable -- it is deprecated on the live resource and the policy auto-publishes on every apply; do not add it back without checking whether the provider has since changed that behavior.requires(the protocol list, e.g.["http"]) is effectively immutable in practice once rules referencing protocol-specific conditions/actions exist -- model a change to it as a destroy/recreate of the policy, not an in-place update.controlsandrequiresare bothset(string)-- the exact accepted value set for each (e.g."forwarding","classification","compression","asm","l7dos") is not independently confirmed against every TMOS version; verify against clouddocs.f5.com/the target device before relying on a specific control or protocol name in production.conditionandactionare modeled as ordered lists, not maps -- unlikeruleitself (keyed on the caller's own rule name), neither carries an independent natural unique key in the live schema, so list order is significant and reordering one is a full list rewrite rather than a per-element diff.- Field combinations inside
condition/actionare type/context dependent (e.g. a selector likehttp_uri = truepaired with a match-type selector likestarts_with = trueand avalueslist) -- this module does not validate combination correctness atterraform validatetime; consult clouddocs.f5.com and the provider's rendered docs for the valid combinations for a given condition/action, per the caveat already carried invariables.tf. ruleisfor_each-keyed on the caller's own rule name (map key) -- adding or removing one rule never re-indexes another, but changing a rule's map key is a destroy/recreate of that one rule's plan entry, not an in-place rename inside the policy.
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(provider floor). - Target partition must already exist (this module does not create partitions).
- Any pool referenced by full path inside a rule's
action.poolmust already exist on the device before this module runs -- this module suite's recommended authoring order builds pools before policies for exactly this reason.
| File | Role |
|---|---|
providers.tf |
required_version >= 1.12.0, pinned F5Networks/bigip ~> 1.28 -- no provider {} block |
variables.tf |
1:1 bigip_ltm_policy argument schema, plus the rule map-of-object schema mirroring the provider's nested rule/condition/action blocks |
main.tf |
Keystone bigip_ltm_policy.this; dynamic "rule" over var.rule, with nested dynamic "condition"/dynamic "action" per rule |
outputs.tf |
name, id, rule_names |
SCOPE.md |
Composite/standalone scope contract: in-scope/out-of-scope boundary, Consumes/Emits tables, required role, F5 prerequisites |
README.md |
This document |
# Caller's root module configures the provider once -- never inside this module.
# provider "bigip" {
# address = var.bigip_address
# username = var.bigip_username
# password = var.bigip_password
# }
module "app_policy" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-policy.git?ref=v1.0.0"
name = "/Common/app-routing-policy"
}rule defaults to {} -- the smallest real call above produces a valid, published, but inert
policy with no rules. Add rule entries to make it actually steer traffic (see the Example
Library below).
Consumes:
| Input | Type | Source module |
|---|---|---|
rule[*].action[*].pool |
string (full-path pool name) |
terraform-bigip-ltm-pool |
Emits:
| Output | Description | Consumed by |
|---|---|---|
name |
Full-path policy name (partition + name) | terraform-bigip-ltm-virtual-server |
id |
Provider-internal id | (rarely consumed directly) |
rule_names |
Names of the rules configured on this policy (keys of var.rule) -- diagnostics only, since rule has no separate provider resource to reference |
(diagnostics / reporting only) |
βΉοΈ Every multi-field
condition/actioncombination below follows the one patternvariables.tfitself documents with confidence (a boolean selector + a match-type selector +values, e.g.http_uri/starts_with/valuesfor a URI-prefix match). Verify any combination against clouddocs.f5.com/the provider's rendered docs for your target TMOS version before relying on it in production -- this module does not validate condition/action semantics atterraform validatetime.
1 Β· Minimal policy, no rules yet
The smallest real call -- the one required argument, no rules.
module "app_policy" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-policy.git?ref=v1.0.0"
name = "/Common/app-routing-policy"
}βΉοΈ
ruledefaults to{},controls/requiresdefault to empty sets -- this produces a valid, published, but inert policy that matches nothing until rules are added.
2 Β· Policy with a description
module "app_policy" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-policy.git?ref=v1.0.0"
name = "/Common/app-routing-policy"
description = "Order-processing app -- URI/host-based routing and maintenance redirects"
}3 Β· Explicit match strategy
module "app_policy" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-policy.git?ref=v1.0.0"
name = "/Common/app-routing-policy"
strategy = "/Common/first-match"
}π‘
strategytakes a full-path strategy name --/Common/first-match,/Common/best-match, and/Common/all-matchare the commonly documented built-ins. Confirm the exact set available on your TMOS version before relying on a specific one (seevariables.tf's own caveat).
4 Β· Policy-level controls
module "waf_aware_policy" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-policy.git?ref=v1.0.0"
name = "/Common/waf-aware-policy"
controls = ["forwarding", "asm"]
}
β οΈ controlsis aset(string)of per-request features this policy activates -- the exact accepted names are not independently confirmed against every TMOS version in this module; verify against clouddocs.f5.com before shipping a control name to production.
5 Β· Policy-level protocol requirement
module "http_only_policy" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-policy.git?ref=v1.0.0"
name = "/Common/http-only-policy"
requires = ["http"]
}π Per SCOPE.md's Provider gotchas,
requiresis effectively immutable in practice once rules exist -- treat a later change to this value as a deliberate destroy/recreate, not a routine edit.
6 Β· Single rule -- URI-prefix match forwarding to a pool
The canonical pattern variables.tf documents directly.
module "app_policy" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-policy.git?ref=v1.0.0"
name = "/Common/app-routing-policy"
rule = {
"api-prefix" = {
condition = [
{
http_uri = true
starts_with = true
values = ["/api"]
}
]
action = [
{
forward = true
pool = "/Common/api-pool"
}
]
}
}
}βΉοΈ
action.poolis consumed by full-path name from a siblingterraform-bigip-ltm-poolmodule'snameoutput -- this module never creates the pool itself, it must already exist.
7 Β· Single rule -- exact host-header match to a different pool
module "app_policy" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-policy.git?ref=v1.0.0"
name = "/Common/app-routing-policy"
rule = {
"host-app2" = {
condition = [
{
http_host = true
equals = true
values = ["app2.example.com"]
}
]
action = [
{
forward = true
pool = "/Common/app2-pool"
}
]
}
}
}8 Β· Rule with multiple ANDed conditions
module "app_policy" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-policy.git?ref=v1.0.0"
name = "/Common/app-routing-policy"
rule = {
"admin-post-only" = {
condition = [
{
http_uri = true
starts_with = true
values = ["/admin"]
},
{
http_method = true
equals = true
values = ["POST"]
}
]
action = [
{
forward = true
pool = "/Common/admin-pool"
}
]
}
}
}π‘ Multiple entries in a rule's
conditionlist are ANDed together unless a condition's ownnot/allflags say otherwise -- both conditions above must match for the action to fire.
9 Β· Rule with a redirect action
module "maintenance_policy" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-policy.git?ref=v1.0.0"
name = "/Common/maintenance-routing-policy"
rule = {
"maintenance-redirect" = {
condition = [
{
http_uri = true
equals = true
values = ["/"]
}
]
action = [
{
redirect = true
location = "https://maintenance.example.com/"
}
]
}
}
}
β οΈ Verifyredirect/locationfield naming against clouddocs.f5.com/the provider's rendered docs for your TMOS version -- as with every condition/action combination in this library, this module does not validate it at plan time.
10 Β· Rule inserting an HTTP request header
module "app_policy" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-policy.git?ref=v1.0.0"
name = "/Common/app-routing-policy"
rule = {
"tag-app-version" = {
condition = [
{
http_uri = true
starts_with = true
values = ["/api"]
}
]
action = [
{
http_header = true
insert = true
tm_name = "X-App-Version"
value = "2.0"
}
]
}
}
}
β οΈ Header-insert field naming (tm_name/valuevs. an alternate combination) should be confirmed against clouddocs.f5.com for your TMOS version before production use -- this is one of the less-certain combinations this module's schema permits without validating semantics.
11 Β· Multiple named rules via for_each
module "app_policy" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-policy.git?ref=v1.0.0"
name = "/Common/app-routing-policy"
rule = {
"api-prefix" = {
condition = [
{ http_uri = true, starts_with = true, values = ["/api"] }
]
action = [
{ forward = true, pool = "/Common/api-pool" }
]
}
"static-prefix" = {
condition = [
{ http_uri = true, starts_with = true, values = ["/static"] }
]
action = [
{ forward = true, pool = "/Common/static-pool" }
]
}
}
}π‘ Keying
ruleon each rule's own name means removing"static-prefix"later never forces Terraform to touch"api-prefix"'s state.
12 Β· Non-/Common partition policy
module "tenant_a_policy" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-policy.git?ref=v1.0.0"
name = "/Tenant-A/app-routing-policy"
rule = {
"api-prefix" = {
condition = [
{ http_uri = true, starts_with = true, values = ["/api"] }
]
action = [
{ forward = true, pool = "/Tenant-A/api-pool" }
]
}
}
}π‘ Partition is folded into
name's full-path convention (/Tenant-A/...), never a separatepartitionvariable -- per this module suite's design decision log, this module invents no implicit partition default.
13 Β· Multiple policies via root-level for_each
This module models exactly one policy per call, so a caller creating several wraps the module
invocation itself at the root, keyed on a stable identifier -- never count.
locals {
policies = {
"app-a" = { name = "/Common/app-a-routing-policy" }
"app-b" = { name = "/Common/app-b-routing-policy" }
}
}
module "app_policies" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-policy.git?ref=v1.0.0"
for_each = local.policies
name = each.value.name
}π‘ Keying on
each.keymeans removingapp-blater never forces Terraform to touchapp-a's state.
14 Β· ποΈ End-to-end composition
A pool feeding this policy's rule action, feeding a virtual server -- the full chain this module participates in.
module "api_pool" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-pool.git?ref=v1.0.0"
name = "/Common/api-pool"
monitors = ["/Common/http_monitor"]
members = {
"10.0.1.10:8080" = {
node = "10.0.1.10:8080"
connection_limit = 0
}
}
}
module "app_policy" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-policy.git?ref=v1.0.0"
name = "/Common/app-routing-policy"
rule = {
"api-prefix" = {
condition = [
{
http_uri = true
starts_with = true
values = ["/api"]
}
]
action = [
{
forward = true
pool = module.api_pool.name
}
]
}
}
}
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.0.1.100:443"
policy_names = [module.app_policy.name]
#...pool/profile/persistence wiring per terraform-bigip-ltm-virtual-server's own README
}ποΈ This module's contribution to the chain is deliberately narrow: it owns exactly the policy record and its rules' condition/action pairs, nothing upstream (the pool the rule forwards to) and nothing downstream (the virtual server that attaches it). Ordering matters -- the pool before the policy, the policy before the virtual server -- and Terraform's implicit dependency graph (via
module.X.namereferences) enforces it automatically.
Summary:
| Variable | Type | Default | Required |
|---|---|---|---|
name |
string |
-- | β |
description |
string |
null |
-- |
strategy |
string |
null |
-- |
controls |
set(string) |
[] |
-- |
requires |
set(string) |
[] |
-- |
rule |
map(object(...)) |
{} |
-- |
Full object schema -- var.rule
variable "rule" {
type = map(object({
description = optional(string)
condition = optional(list(object({
address = optional(bool)
all = optional(bool)
app_service = optional(string)
browser_type = optional(bool)
browser_version = optional(bool)
case_insensitive = optional(bool)
case_sensitive = optional(bool)
cipher = optional(bool)
cipher_bits = optional(bool)
client_accepted = optional(bool)
client_ssl = optional(bool)
code = optional(bool)
common_name = optional(bool)
contains = optional(bool)
continent = optional(bool)
country_code = optional(bool)
country_name = optional(bool)
cpu_usage = optional(bool)
datagroup = optional(string)
device_make = optional(bool)
device_model = optional(bool)
domain = optional(bool)
ends_with = optional(bool)
equals = optional(bool)
exists = optional(bool)
expiry = optional(bool)
extension = optional(bool)
external = optional(bool)
geoip = optional(bool)
greater = optional(bool)
greater_or_equal = optional(bool)
host = optional(bool)
http_basic_auth = optional(bool)
http_cookie = optional(bool)
http_header = optional(bool)
http_host = optional(bool)
http_method = optional(bool)
http_referer = optional(bool)
http_set_cookie = optional(bool)
http_status = optional(bool)
http_uri = optional(bool)
http_user_agent = optional(bool)
http_version = optional(bool)
index = optional(number)
internal = optional(bool)
isp = optional(bool)
last_15secs = optional(bool)
last_1min = optional(bool)
last_5mins = optional(bool)
less = optional(bool)
less_or_equal = optional(bool)
local = optional(bool)
major = optional(bool)
matches = optional(bool)
minor = optional(bool)
missing = optional(bool)
mss = optional(bool)
not = optional(bool)
org = optional(bool)
password = optional(bool)
path = optional(bool)
path_segment = optional(bool)
port = optional(bool)
present = optional(bool)
protocol = optional(bool)
query_parameter = optional(bool)
query_string = optional(bool)
region_code = optional(bool)
region_name = optional(bool)
remote = optional(bool)
request = optional(bool)
response = optional(bool)
route_domain = optional(bool)
rtt = optional(bool)
scheme = optional(bool)
server_name = optional(bool)
ssl_cert = optional(bool)
ssl_client_hello = optional(bool)
ssl_extension = optional(bool)
ssl_server_handshake = optional(bool)
ssl_server_hello = optional(bool)
starts_with = optional(bool)
tcp = optional(bool)
text = optional(bool)
tm_name = optional(string)
unnamed_query_parameter = optional(bool)
user_agent_token = optional(bool)
username = optional(bool)
value = optional(bool)
values = optional(list(string))
version = optional(bool)
vlan = optional(bool)
vlan_id = optional(bool)
})), [])
action = optional(list(object({
app_service = optional(string)
application = optional(string)
asm = optional(bool)
avr = optional(bool)
cache = optional(bool)
carp = optional(bool)
category = optional(string)
classify = optional(bool)
clone_pool = optional(string)
code = optional(number)
compress = optional(bool)
connection = optional(bool)
content = optional(string)
cookie_hash = optional(bool)
cookie_insert = optional(bool)
cookie_passive = optional(bool)
cookie_rewrite = optional(bool)
decompress = optional(bool)
defer = optional(bool)
destination_address = optional(bool)
disable = optional(bool)
domain = optional(string)
enable = optional(bool)
expiry = optional(string)
expiry_secs = optional(number)
expression = optional(string)
extension = optional(string)
facility = optional(string)
forward = optional(bool)
from_profile = optional(string)
hash = optional(bool)
host = optional(string)
http = optional(bool)
http_basic_auth = optional(bool)
http_cookie = optional(bool)
http_header = optional(bool)
http_host = optional(bool)
http_referer = optional(bool)
http_reply = optional(bool)
http_set_cookie = optional(bool)
http_uri = optional(bool)
ifile = optional(string)
insert = optional(bool)
internal_virtual = optional(string)
ip_address = optional(string)
key = optional(string)
l7dos = optional(bool)
length = optional(number)
location = optional(string)
log = optional(bool)
ltm_policy = optional(bool)
member = optional(string)
message = optional(string)
netmask = optional(string)
nexthop = optional(string)
node = optional(string)
offset = optional(number)
path = optional(string)
pem = optional(bool)
persist = optional(bool)
pin = optional(bool)
policy = optional(string)
pool = optional(string)
port = optional(number)
priority = optional(string)
profile = optional(string)
protocol = optional(string)
query_string = optional(string)
rateclass = optional(string)
redirect = optional(bool)
remove = optional(bool)
replace = optional(bool)
request = optional(bool)
request_adapt = optional(bool)
reset = optional(bool)
response = optional(bool)
response_adapt = optional(bool)
scheme = optional(string)
script = optional(string)
select = optional(bool)
server_ssl = optional(bool)
set_variable = optional(bool)
shutdown = optional(bool)
snat = optional(string)
snatpool = optional(string)
source_address = optional(bool)
ssl_client_hello = optional(bool)
ssl_server_handshake = optional(bool)
ssl_server_hello = optional(bool)
ssl_session_id = optional(bool)
status = optional(number)
tcl = optional(bool)
tcp_nagle = optional(bool)
text = optional(string)
timeout = optional(number)
tm_name = optional(string)
uie = optional(bool)
universal = optional(bool)
value = optional(string)
virtual = optional(string)
vlan = optional(string)
vlan_id = optional(number)
wam = optional(bool)
write = optional(bool)
})), [])
}))
default = {}
}| Field | Type | Notes |
|---|---|---|
description |
string |
Optional per-rule description. |
condition |
list(object(...)) |
ANDed match criteria (unless a condition's own not/all flags say otherwise); no independent unique key, so it is a list, not a map. |
action |
list(object(...)) |
Steering behavior applied when the rule matches; same list-not-map reasoning as condition. |
Both condition and action objects are direct 1:1 mirrors of the live bigip_ltm_policy
provider schema -- consult clouddocs.f5.com and the provider's rendered docs for the specific
field combinations valid for a given condition/action "type."
| Output | Description | Sensitive |
|---|---|---|
name |
Full-path LTM policy name (partition + name) -- primary cross-reference key | No |
id |
Provider-internal id of the LTM policy | No |
rule_names |
Names of the rules configured on this policy (keys of var.rule) -- diagnostics only, since rule has no separate provider resource to reference |
No |
ruleisfor_each-keyed on the caller's own rule name, nevercount.main.tfiteratesvar.rule(amap(object(...))) and renders onedynamic "rule"block per entry, withname = rule.key-- adding or removing one rule never re-indexes another rule's plan entry.- No separate provider resource for a rule. Unlike
terraform-bigip-ltm-pool's pool-member attachments,rule/condition/actionare all nested repeating blocks insidebigip_ltm_policyitself, per SCOPE.md's Design intent -- there is exactly one managed resource in this module. conditionandactionare ordered lists, not maps. Neither has an independent natural unique key in the live schema (unlikerule, which is keyed on its own name), so both are modeled as lists that mirror the provider's own repeating-block order.try(x, null)guards every optional field -- the policy-leveldescription/strategy, and every one of the ~90conditionfields and ~90actionfields -- so an absent value never renders a spurious empty string orfalseinto the API payload.- Pools are consumed, never created, here.
rule[*].action[*].poolis a plainstringfull path fromterraform-bigip-ltm-pool; this module does not validate that the referenced pool exists -- this module suite's recommended authoring order (pools before policies) is a human/pipeline concern, not something this module's type system enforces. published_copyis structurally absent, not merely defaulted. It is not declared invariables.tfat all, per SCOPE.md's Provider gotchas -- callers cannot accidentally wire a value into a deprecated, auto-publishing field.- Full-path identity is partition-specific.
namein the form/Partition/nameis the practical cross-reference keyterraform-bigip-ltm-virtual-serverconsumes.
| Concern | This module's default | Opt-out (caller must type extra) |
|---|---|---|
| Partition scope | No implicit default is invented -- name must carry the full path (/Partition/name) explicitly |
Caller always supplies the full path in name |
| Rule set | rule defaults to {} -- a policy with no rules is valid but inert (matches nothing, steers nothing) |
Caller adds rule entries explicitly to make the policy do anything |
| Protocol / controls | controls and requires both default to an empty set -- no feature activation or protocol requirement is silently assumed |
Caller explicitly lists the controls/protocols this policy actually needs |
| Deprecated fields | published_copy is not exposed as a module variable at all -- callers cannot depend on a no-op, deprecated field |
N/A -- structural, not a toggle |
| Secrets | Not applicable -- bigip_ltm_policy carries no secret-shaped attribute |
N/A |
cd C:\GitHubCode\newf5modules\bigip\terraform-bigip-ltm-policy
terraform init -backend=false
terraform validate
terraform fmt -check
Remove-Item -Recurse -Force.terraform,.terraform.lock.hcl -ErrorAction SilentlyContinuePin consuming module calls to ?ref=v1.0.0 (or the current tagged release) rather than an
unpinned branch reference.
This module's local proof gate is plan-only, schema-level validation:
| 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 the rule/condition/action object shapes are internally consistent |
That a given condition/action field combination is semantically valid for its "type," or that a referenced pool actually exists by full path |
terraform fmt -check |
Canonical HCL formatting | Anything about runtime behavior |
Only a real terraform plan/apply against an authenticated bigip provider instance confirms
the target partition exists, that a rule's condition/action combination is accepted by the device,
and that a referenced pool resolves by full path. That step is a root-module/pipeline concern,
never something this authoring process exercises.
module.app_policy.bigip_ltm_policy.this: Creating...
module.app_policy.bigip_ltm_policy.this: Creation complete after 1s [id=/Common/app-routing-policy]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
id = "/Common/app-routing-policy"
name = "/Common/app-routing-policy"
rule_names = [
"api-prefix",
]
| Symptom | Cause | Fix |
|---|---|---|
terraform validate fails with Unsupported argument inside a condition/action block |
A field name was misspelled or doesn't exist in this module's fixed object schema |
Check the field name against the Full object schema in π₯ Inputs, or the live provider docs |
BIG-IP rejects the policy at apply with a generic API error |
A condition/action field combination is syntactically valid Terraform but not semantically valid for its "type" on the target TMOS version |
This module does not validate combination correctness -- confirm the combination against clouddocs.f5.com |
Plan shows a full destroy/recreate of the whole policy after changing requires |
requires is effectively immutable in practice once rules exist (see SCOPE.md Provider gotchas) |
Expected -- confirm the change was deliberate; this is not a module bug |
| Plan shows one rule fully replaced instead of updated | That rule's for_each key (its map name) changed |
Expected -- rule identity is its map key; rename it deliberately and expect a replace, not an in-place rename |
| Virtual server fails to attach this policy | policy_names on the virtual server doesn't match this module's name output exactly, or the two live in different partitions |
Wire policy_names = [module.app_policy.name] directly rather than retyping the full path |
| Policy applies cleanly but traffic never reaches the expected pool | The pool referenced in rule[*].action[*].pool doesn't yet exist by that exact full path on the device |
Confirm the sibling terraform-bigip-ltm-pool module applied successfully first, and that partitions match |
connection refused on first apply against a cloud-deployed BIG-IP |
Management interface reachable only on a non-443 port (common on single-NIC AWS/Azure/GCP deployments) | Set the provider's port argument (env BIGIP_PORT) at the root module -- not a concern of this module |
F5Networks/bigipprovider registry docs -- bigip_ltm_policy- clouddocs.f5.com -- LTM Local Traffic Policies
- This module's
SCOPE.md - Sibling modules:
terraform-bigip-ltm-pool,terraform-bigip-ltm-virtual-server,terraform-bigip-ltm-irule,terraform-bigip-ltm-datagroup