Manages a single
bigip_ltm_profile_bot_defenseresource — the Bot Defense profile a BIG-IP virtual server attaches to detect and mitigate automated/bot traffic. TargetsF5Networks/bigip~> 1.28.
- 🤖 Manages exactly one
bigip_ltm_profile_bot_defenseresource — a single flat Bot Defense profile with no nested blocks and nofor_eachchildren. - 🎯 Seeds mitigation/verification defaults from a
template(balanced,relaxed,strict). - 🚦 Sets enforcement posture explicitly via
enforcement_mode(transparentmonitor-only vs.blockingactively enforced). - 🔗 Inherits settings from a parent profile via
defaults_from— defaults to the device-shipped/Common/bot-defensebaseline, but can chain to a sibling custom profile instead. - 🧵 Emits
name/idfor full-path consumption byterraform-bigip-ltm-virtual-server.
💡 Why it matters: Bot Defense is one of the few LTM profile types where the default value materially changes the device's security posture. Leaving
enforcement_mode(ortemplate)nulldefers to whatever BIG-IP applies server-side — which may not be the deliberate "actively blocking" stance a production application needs. This module surfaces that decision as an explicit, typed input rather than letting it hide behind a silent default.
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
BASE["/Common/bot-defense (device-shipped baseline)"]:::ext
THIS["terraform-bigip-ltm-profile-bot-defense"]:::this
SAAS["terraform-bigip-saas-bot-defense-profile"]:::ext
HTTP["terraform-bigip-ltm-profile-http"]:::sib
CSSL["terraform-bigip-ltm-profile-client-ssl"]:::sib
SSSL["terraform-bigip-ltm-profile-server-ssl"]:::sib
TCP["terraform-bigip-ltm-profile-tcp"]:::sib
VS["terraform-bigip-ltm-virtual-server"]:::target
BASE -- "defaults_from (optional)" --> THIS
THIS -- "name (full path)" --> VS
HTTP -- "name (full path)" --> VS
CSSL -- "name (full path)" --> VS
SSSL -- "name (full path)" --> VS
TCP -- "name (full path)" --> VS
SAAS -. "separate family, not consumed here".-> VS
classDef this fill:#E4002B,color:#ffffff,stroke:#333;
classDef target fill:#000000,color:#ffffff,stroke:#333;
classDef sib fill:#e0e0e0,color:#222222,stroke:#999;
classDef ext fill:#f5f5f5,color:#222222,stroke:#999,stroke-dasharray: 3 3;
This module (red) owns only the Bot Defense profile record itself. terraform-bigip-ltm-virtual-server
(black — the consuming target) attaches this profile, alongside sibling terraform-bigip-ltm-profile-*
modules, by full-path name. terraform-bigip-saas-bot-defense-profile is a related but entirely
separate module family wrapping F5's cloud/SaaS Bot Defense integration — it is never the same
Terraform resource as this module.
flowchart TB
subgraph Inputs
NAME["name"]
DEF["defaults_from"]
DESC["description"]
TMPL["template"]
ENF["enforcement_mode"]
end
RES["bigip_ltm_profile_bot_defense.this"]:::this
NAME --> RES
DEF --> RES
DESC --> RES
TMPL --> RES
ENF --> RES
RES -- "emits" --> OUT_NAME["output: name"]
RES -- "emits" --> OUT_ID["output: id"]
classDef this fill:#E4002B,color:#ffffff,stroke:#333;
| Resource | Count | Notes |
|---|---|---|
bigip_ltm_profile_bot_defense.this |
1 | Keystone; flat attribute set (name, defaults_from, description, template, enforcement_mode) — no nested blocks, no for_each children |
| Requirement | Value |
|---|---|
| Terraform | >= 1.12.0 |
| Provider | F5Networks/bigip ~> 1.28 — re-verify against the current Terraform Registry listing before each new module wave (F5 ships frequent minor releases) |
| Provider block | None — the caller configures provider "bigip" {}; this module never declares one |
Schema notes that bite:
- Full-path
nameis BIG-IP's real identity (/Partition/name). Renaming this profile or moving it to a different partition forces destroy/recreate, not an in-place update. templateandenforcement_modeare Optional+Computed with no documented literal default — leaving eithernulldefers to whatever BIG-IP applies server-side, which this module does not control or predict.descriptionis likewise Optional+Computed when leftnull.defaults_fromdefaults to/Common/bot-defensein this module because that matches the provider's own documented default — it is not an invented partition default.
Resource Administrator. Unlike most application-layer LTM profiles (where Manager scoped to
the target partition is normally sufficient), a Bot Defense profile shapes device-wide
bot-mitigation and request-enforcement posture — template-seeded detection/mitigation defaults
and the transparent-vs-blocking enforcement decision — broadly enough that partition-scoped
Manager access is insufficient. Provision this role deliberately, not as a default grant.
- iControl REST enabled and reachable on the target device.
- TMOS
>= v12.1.1(provider floor per house standard). - Bot Defense is an ASM/Bot-Defense-provisioned feature — the target device must have the
relevant security module provisioned before this profile has any practical mitigation effect.
This authoring pass verified the Terraform-facing argument schema only; confirm the exact
minimum TMOS version and provisioning requirement for your target release against
clouddocs.f5.combefore relying on this in production — that device-side provisioning prerequisite is not somethingterraform validateor the provider schema can surface. - Target partition must already exist before this module runs.
- The parent profile named in
defaults_from(default/Common/bot-defense, which ships built-in) must already exist on the device — any custom parent must be created and verified first.
terraform-bigip-ltm-profile-bot-defense/
├── providers.tf # required_version, F5Networks/bigip ~> 1.28 pin — no provider block
├── variables.tf # name, defaults_from, description, template, enforcement_mode
├── main.tf # bigip_ltm_profile_bot_defense.this — flat keystone, no children
├── outputs.tf # name, id
├── SCOPE.md # cross-module contract (this doc's source of truth for role/prereqs)
└── README.md # this file
# Caller configures the provider — this module never does.
provider "bigip" {
address = var.bigip_address
username = var.bigip_username
password = var.bigip_password
}
module "bot_defense_profile" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0"
name = "/Common/app01-bot-defense"
enforcement_mode = "blocking"
}Consumes
| Input | Type | Source module |
|---|---|---|
defaults_from (optional) |
string (full-path name) |
/Common/bot-defense (device-shipped baseline, module default) or a sibling terraform-bigip-ltm-profile-bot-defense instance |
Emits
| Output | Description | Consumed by |
|---|---|---|
name |
Full-path name of the Bot Defense profile (e.g. /Common/my-bot-defense-profile) — the practical cross-reference key |
terraform-bigip-ltm-virtual-server |
id |
Provider-internal id of the Bot Defense profile | rarely consumed directly |
1 · Minimal blocking profile
The smallest real call for a production posture — explicit enforcement_mode rather than relying
on whatever BIG-IP defaults to server-side.
module "bot_defense_profile" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0"
name = "/Common/app01-bot-defense"
enforcement_mode = "blocking"
}
⚠️ Omittingenforcement_mode(leaving itnull) defers to a BIG-IP-side default this module does not control — set it explicitly for any profile meant to actively block bot traffic.
2 · Transparent (monitor-only) staging profile
Rolling out Bot Defense against a new application without yet blocking traffic — observe detections before flipping to enforcement.
module "bot_defense_staging" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0"
name = "/Common/app02-bot-defense-staging"
enforcement_mode = "transparent"
description = "Staged rollout — monitor only, no blocking action taken"
}ℹ️
transparentmode does not block detected bot traffic. Use this deliberately for a staged/observe-first rollout, not as a lasting production posture.
3 · Strict template with blocking enforcement
Highest sensitivity mitigation defaults, actively enforced — appropriate for a high-value/high-risk application surface.
module "bot_defense_strict" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0"
name = "/Common/payments-bot-defense"
template = "strict"
enforcement_mode = "blocking"
description = "Payments API — strict template, blocking enforcement"
}4 · Relaxed template for a low-risk internal app
An internal-only application where false positives are more costly than a lighter mitigation touch.
module "bot_defense_internal" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0"
name = "/Common/intranet-bot-defense"
template = "relaxed"
enforcement_mode = "blocking"
}5 · Explicit balanced template
Documenting the middle-ground posture explicitly rather than leaving template unset and
inheriting whatever BIG-IP defaults to.
module "bot_defense_balanced" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0"
name = "/Common/portal-bot-defense"
template = "balanced"
enforcement_mode = "blocking"
}💡 Setting
templateexplicitly — even to the middle-ground"balanced"value — documents the intended posture in code instead of leaving it to whatever the device happens to default to.
6 · Custom description for change-audit trail
Using the free-text description field to record ownership/change context for reviewers.
module "bot_defense_audited" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0"
name = "/Common/checkout-bot-defense"
enforcement_mode = "blocking"
description = "Owned by Checkout team — CR-4821 — blocking since 2026-05-01"
}7 · Chaining defaults_from to a sibling custom profile
A hardened base profile that other application-specific profiles inherit from, instead of every
profile inheriting directly from the system-shipped /Common/bot-defense baseline.
module "bot_defense_base" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0"
name = "/Common/org-bot-defense-base"
template = "strict"
enforcement_mode = "blocking"
}
module "bot_defense_app" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0"
name = "/Common/app03-bot-defense"
defaults_from = module.bot_defense_base.name
enforcement_mode = "blocking"
}💡 Wiring
defaults_fromtomodule.bot_defense_base.name(rather than a literal string) lets Terraform's implicit dependency graph order creation correctly — the base profile before the one that inherits from it.
8 · Non-/Common partition placement
Placing the profile in a tenant-scoped partition rather than the default /Common.
module "bot_defense_tenant_a" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0"
name = "/Tenant-A/app-bot-defense"
enforcement_mode = "blocking"
}🔒 The target partition (
/Tenant-Ahere) must already exist before this module runs — this module does not create partitions, only the profile inside one.
9 · Overriding defaults_from away from the system baseline
Pointing at a pre-existing, already-hardened custom parent profile instead of the device-shipped
/Common/bot-defense default.
module "bot_defense_custom_parent" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0"
name = "/Common/app04-bot-defense"
defaults_from = "/Common/org-hardened-bot-defense"
enforcement_mode = "blocking"
}
⚠️ The full path supplied todefaults_frommust already exist on the device (or be created by another Terraform resource ordered ahead of this one) — this module only references it by name.
10 · Strict template paired with transparent mode
High-fidelity detection signal without yet enforcing — useful when validating a strict template's
false-positive rate before switching to blocking.
module "bot_defense_strict_observe" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0"
name = "/Common/app05-bot-defense-observe"
template = "strict"
enforcement_mode = "transparent"
description = "Validating strict template false-positive rate before blocking cutover"
}11 · Caller-side for_each over multiple profiles
This module owns a single flat profile; a fleet of profiles is composed by the caller, not by
for_each inside this module.
locals {
bot_defense_profiles = {
app06 = { name = "/Common/app06-bot-defense", enforcement_mode = "blocking" }
app07 = { name = "/Common/app07-bot-defense", enforcement_mode = "transparent" }
}
}
module "bot_defense_fleet" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0"
for_each = local.bot_defense_profiles
name = each.value.name
enforcement_mode = each.value.enforcement_mode
}ℹ️ Keying
for_eachon a stable map key (app06,app07) — never a list index — means adding a seventh profile never forces Terraform to touch the first two.
12 · Wiring the emitted name into a virtual server (illustrative)
Illustrates the cross-module contract — this module's name output is the value
terraform-bigip-ltm-virtual-server consumes; it does not create the virtual server itself.
module "bot_defense_profile" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0"
name = "/Common/app08-bot-defense"
enforcement_mode = "blocking"
}
# Illustrative only — see terraform-bigip-ltm-virtual-server's own README for its real variable
# shape. The point of this example is the cross-reference, not the virtual-server call itself.
# virtual_server_profiles = [module.bot_defense_profile.name]13 · Explicit posture documented via description + template + mode together
Combining all three optional posture-shaping fields deliberately, rather than leaving any of them to defer to a BIG-IP-side default.
module "bot_defense_documented" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0"
name = "/Common/app09-bot-defense"
template = "balanced"
enforcement_mode = "blocking"
description = "Balanced template, blocking enforcement — approved 2026-06-30, ticket CR-5010"
}🏗️ 14 · End-to-end composition
A base hardened profile, an application-specific profile chained from it, and the illustrative hand-off to the virtual-server module that ultimately attaches it — the full path from this module's output to its real-world consumer.
provider "bigip" {
address = var.bigip_address
username = var.bigip_username
password = var.bigip_password
}
# 1. Org-wide hardened base profile — strict template, blocking, /Common.
module "bot_defense_base" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0"
name = "/Common/org-bot-defense-base"
template = "strict"
enforcement_mode = "blocking"
description = "Org-wide hardened Bot Defense baseline — all app profiles inherit from this"
}
# 2. Application-specific profile chained from the hardened base.
module "bot_defense_checkout" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0"
name = "/Common/checkout-bot-defense"
defaults_from = module.bot_defense_base.name
enforcement_mode = "blocking"
description = "Checkout app — inherits org hardened baseline"
}
# 3. Sibling LTM profiles the virtual server also attaches (each its own module family).
module "checkout_http_profile" {
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-http.git?ref=v1.0.0"
name = "/Common/checkout-http"
}
# 4. The virtual server that ties everything together — illustrative shape only; see
# terraform-bigip-ltm-virtual-server's own README for its real variable contract.
# module "checkout_vs" {
# source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-virtual-server.git?ref=v1.0.0"
#
# name = "/Common/checkout-vs"
# destination = "10.20.30.40:443"
# profiles = [
# module.bot_defense_checkout.name,
# module.checkout_http_profile.name,
# ]
# }💡 The base-then-app chain (
module.bot_defense_base.name→defaults_from) is the pattern to reach for whenever more than one application needs the same hardened Bot Defense posture — author the shared baseline once, then chain application-specific overrides from it.
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
name |
string |
— | yes | Full-path name of the Bot Defense profile, e.g. /Common/my-bot-defense-profile |
defaults_from |
string |
"/Common/bot-defense" |
no | Full-path name of the parent profile this profile inherits from |
description |
string |
null |
no | Free-text description; provider-computed if left null |
template |
string |
null |
no | One of balanced, relaxed, strict; provider-computed if left null |
enforcement_mode |
string |
null |
no | One of transparent, blocking; provider-computed if left null |
Full variable definitions
The live bigip_ltm_profile_bot_defense schema is a flat attribute set with no nested blocks, so
there are no object-typed fields in this module — every variable below is a scalar string.
variable "name" {
type = string
# Immutable in practice: BIG-IP's full-path identity is partition-specific, so renaming or
# moving this profile between partitions forces destroy/recreate rather than an in-place update.
}
variable "defaults_from" {
type = string
default = "/Common/bot-defense"
# Provider default: the system-supplied "/Common/bot-defense" profile — baked in here because
# it matches the provider's own documented default.
}
variable "description" {
type = string
default = null
# Provider-computed if left null; no literal default value is documented.
}
variable "template" {
type = string
default = null
# One of: "balanced", "relaxed", "strict". Provider-computed if left null.
validation {
condition = var.template == null ? true : contains(["balanced", "relaxed", "strict"], var.template)
error_message = "template must be one of: balanced, relaxed, strict."
}
}
variable "enforcement_mode" {
type = string
default = null
# One of: "transparent", "blocking". Provider-computed if left null.
validation {
condition = var.enforcement_mode == null ? true : contains(["transparent", "blocking"], var.enforcement_mode)
error_message = "enforcement_mode must be one of: transparent, blocking."
}
}| Output | Description | Sensitive / Conditional |
|---|---|---|
name |
Full-path name of the Bot Defense profile, e.g. /Common/my-bot-defense-profile. Consumed by full-path name from terraform-bigip-ltm-virtual-server. |
no |
id |
Provider-internal id of the Bot Defense profile. | no |
main.tfis a total, unconditional passthrough: every variable maps directly onto the matchingbigip_ltm_profile_bot_defense.thisargument — nodynamicblocks, notry(x, null)guards, because the live schema has no nested/repeating structure to guard against.- There is no
for_eachin this module because the resource owns no child collection; a fleet of profiles is a caller-side composition concern (see Example 11). - Ordering: when
defaults_fromchains to a sibling instance of this same module (Example 7), wire it viamodule.<base>.namerather than a literal string — this lets Terraform's implicit dependency graph create the base profile before the one that inherits from it. - Full-path identity is immutable.
name's partition segment is part of BIG-IP's real identity; changing it is a destroy/recreate, not an in-place rename, for both this profile and anything referencing its old full path. - No AS3/DO involvement — this is a discrete LTM resource, not a declarative-JSON escape hatch.
| Concern | Secure default in this module | Opt-out (caller must type extra) |
|---|---|---|
| Enforcement posture | enforcement_mode is left null (provider-computed) rather than the module inventing a hardcoded default — the README and variable description flag that a null value does not guarantee a blocking stance |
Caller sets enforcement_mode = "blocking" explicitly for a deliberate production enforcement posture (recommended for anything internet-facing) |
| Mitigation template | template is left null (provider-computed); the module never silently picks a template on the caller's behalf |
Caller sets template explicitly (balanced/relaxed/strict) to document a deliberate posture |
| Parent profile / partition scope | defaults_from defaults to /Common/bot-defense only because that matches the provider's own documented default — no invented partition default |
Caller supplies a custom defaults_from full path (own partition or a hardened custom parent) |
| Secrets | N/A — this resource's live schema exposes no secret-shaped field | — |
cd C:\GitHubCode\newf5modules\bigip\terraform-bigip-ltm-profile-bot-defense
terraform init -backend=false
terraform validate
terraform fmt -checkPin ?ref=v1.0.0 (or the current release tag) whenever consuming this module via
source = "git::https://github.com/microsoftexpert/terraform-bigip-ltm-profile-bot-defense.git?ref=v1.0.0".
terraform init -backend=false && terraform validate && terraform fmt -check is a plan-only,
schema-level proof gate — it confirms the .tf files are internally consistent (types,
required_version, provider pin, closed-enum validation{} blocks) without ever contacting a
real device.
What it does not prove, and what only a human-run terraform plan/apply against a real
BIG-IP (from CI, never from this authoring process) can:
- That the
defaults_fromparent profile actually exists on the target device/partition. - That ASM/Bot Defense is provisioned on the target device, so the profile has a real mitigation effect rather than merely existing as a configuration object.
- That the applying credential actually holds Resource Administrator rights.
$ terraform apply
# module.bot_defense_profile.bigip_ltm_profile_bot_defense.this will be created
+ resource "bigip_ltm_profile_bot_defense" "this" {
+ defaults_from = "/Common/bot-defense"
+ enforcement_mode = "blocking"
+ id = (known after apply)
+ name = "/Common/app01-bot-defense"
}
Plan: 1 to add, 0 to change, 0 to destroy.
Outputs:
id = "/Common/app01-bot-defense"
name = "/Common/app01-bot-defense"
| Symptom | Cause | Fix |
|---|---|---|
defaults_from reference not found at apply |
Custom parent Bot Defense profile hasn't been created/applied yet | Ensure the parent module/resource applies first, or wire it via a module output (module.<base>.name) so Terraform's dependency graph orders it correctly |
| Profile creates successfully but has no mitigation effect | ASM/Bot Defense is not provisioned on the target device | Provision ASM/Bot Defense on the BIG-IP before relying on enforcement — see F5 BIG-IP Prerequisites |
| Renaming this profile forces an unexpected destroy/recreate | Full-path name (partition + name) is immutable BIG-IP identity, not a mutable label |
Treat name/partition as effectively write-once; cut over to a new resource instead of an in-place rename |
template must be one of: balanced, relaxed, strict validation error |
Caller passed an unsupported template string |
Use one of the three documented values, or leave template unset (null) |
| Virtual server can't resolve this profile's full path | Wrong partition or a typo in the literal string passed downstream | Confirm the full path including partition; wire the virtual server's profile list from this module's name output rather than a hand-typed literal |
F5Networks/bigipprovider —bigip_ltm_profile_bot_defenseresource (Terraform Registry).clouddocs.f5.com— Bot Defense feature overview and ASM/Bot-Defense provisioning guidance.- Sibling modules:
terraform-bigip-ltm-virtual-server(consumer),terraform-bigip-saas-bot-defense-profile(related but separate cloud/SaaS Bot Defense family),terraform-bigip-waf-policy(related ASM family). - This module's
SCOPE.md.