Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ”΅ F5OS Interface Terraform Module

Manages a single physical/logical network interface (f5os_interface) on an F5OS platform β€” VELOS chassis partition or rSeries appliance β€” targeting the F5Networks/f5os provider ~> 1.10 (verified against v1.12.0).

Terraform Provider Module Type Resources Posture


🧩 Overview

  • 🏷️ Manages exactly one f5os_interface resource β€” enable/disable state, native (untagged) VLAN, and trunk (tagged) VLAN set on an already-present physical or logical interface.
  • 🧱 Standalone module: single keystone resource named this, no children, no for_each inside the module boundary.
  • 🌐 Applies identically to a VELOS chassis partition or an rSeries appliance β€” the f5os_interface schema carries no platform-specific argument; only the expected name format convention differs (blade/port on VELOS, e.g. 1/1.0; port only on rSeries, e.g. 1.0).
  • πŸ”— Consumes VLAN IDs (as plain numbers) that a sibling terraform-f5os-vlan module owns, and emits name for a future terraform-f5os-lag module to consume as a LAG member.
  • 🚫 No secrets, no nested blocks, no universal tail (tags/timeouts) β€” a thin, flat passthrough over four settable arguments.

πŸ’‘ Why it matters: an interface is the first configurable object between a raw physical port and everything built on top of it (VLAN tagging, LAG membership, tenant traffic) β€” configuring it as a single, typed, reviewable Terraform resource catches a missing interface identifier or an unintended VLAN change at terraform validate/plan time instead of an opaque failure against a real chassis or appliance.


❀️ 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
 IFACE["terraform-f5os-interface"]:::this
 VLAN["terraform-f5os-vlan"]:::sibling
 LAG["terraform-f5os-lag"]:::sibling
 PLATFORM["F5OS platform (VELOS chassis partition or rSeries appliance)"]:::target

 VLAN -->|"vlan_id consumed by (VLAN tagging)"| IFACE
 VLAN -->|"vlan_id consumed by (VLAN tagging)"| LAG
 IFACE -->|"name consumed by (LAG member composition)"| LAG
 IFACE -->|"configures existing interface on"| PLATFORM

 classDef this fill:#E4002B,color:#ffffff,stroke:#8f0016,stroke-width:1px;
 classDef target fill:#1B2A4A,color:#ffffff,stroke:#0d1626,stroke-width:1px;
 classDef sibling fill:#E8E8E8,color:#1a1a1a,stroke:#b5b5b5,stroke-width:1px;
Loading

terraform-f5os-interface (red) sits in the networking domain alongside terraform-f5os-vlan and terraform-f5os-lag. It consumes VLAN IDs (as plain numbers, not enforced Terraform references) from terraform-f5os-vlan for its own native_vlan/trunk_vlans arguments, and its name output is the value a future terraform-f5os-lag module would collect into its members set when composing a Link Aggregation Group from physical interfaces. It has no upstream dependency of its own within this module's Terraform contract β€” VLAN references are caller-wired numbers, not module inputs that require a sibling module to exist first.


🧬 What this builds

graph LR
 subgraph Inputs["Inputs"]
 VAR["var.interface { name, enabled, native_vlan, trunk_vlans }"]
 end

 RES["f5os_interface.this"]:::this

 subgraph Outputs["Outputs"]
 OUT_ID["id"]
 OUT_NAME["name"]
 OUT_STATUS["status"]
 OUT_ENABLED["enabled"]
 OUT_NVLAN["native_vlan"]
 OUT_TVLANS["trunk_vlans"]
 end

 VAR -->|"name, enabled, native_vlan, trunk_vlans"| RES
 RES -->|"exposes"| OUT_ID
 RES -->|"exposes"| OUT_NAME
 RES -->|"exposes"| OUT_STATUS
 RES -->|"exposes"| OUT_ENABLED
 RES -->|"exposes"| OUT_NVLAN
 RES -->|"exposes"| OUT_TVLANS

 classDef this fill:#E4002B,color:#ffffff,stroke:#8f0016,stroke-width:1px;
Loading

Resource inventory

Resource Terraform address Cardinality
f5os_interface f5os_interface.this Exactly 1 per module call

βœ… Provider / Versions

Item Value
Terraform floor >= 1.12.0
Provider pin F5Networks/f5os ~> 1.10 β€” verified against the live Terraform Registry listing; terraform init resolved v1.12.0 during authoring, which satisfies this constraint
Provider block None in this module β€” the caller configures provider "f5os" {} once, in the root module, targeting a single already-authenticated VELOS chassis controller or rSeries appliance
Platform context Both β€” VELOS chassis partition and rSeries appliance. f5os_interface's schema carries no partition-specific or appliance-specific argument; only the expected name format convention differs (blade/port on VELOS, port only on rSeries)

Schema notes that bite (verified against the live f5os_interface schema β€” both the rendered registry doc and a terraform providers schema -json dump, which agree):

  • No argument on this resource is marked required in the live schema β€” name included. This is very likely a schema oversight rather than intentional design: the closely related sibling resource f5os_lag marks its own name argument Required. Terraform's type system cannot catch this on its own since name is validly typed as optional(string) here β€” omitting it will pass terraform validate and leave the resource unable to identify which interface to configure, very likely failing (or applying ambiguously) at apply time against a real chassis or appliance. Always set interface.name explicitly.
  • enabled is Optional+Computed β€” when left unset (null), F5OS supplies its own current/computed value for the interface rather than the provider defaulting to a fixed boolean.
  • native_vlan and trunk_vlans are Optional (not computed) with no provider-side default β€” left null, no native VLAN or trunk VLANs are configured.
  • trunk_vlans is a Set of Number β€” order-insensitive; no diff churn should occur if the caller enumerates the same VLAN IDs in a different order.
  • Import uses the bare interface name as the resource ID (terraform import f5os_interface.test-import 1.0), not a composite string.
  • id and status are Read-Only/computed β€” never settable inputs.

πŸ”‘ Required F5OS User Role / Chassis Partition Access

Least-privilege role: operator, per the f5os_user resource's documented role set (role β€” "Specifies primary role assigned to the user (e.g., admin, operator, user)"). Network interface management privilege scoped to the target chassis partition (VELOS) or the appliance itself (rSeries) is sufficient for routine interface enable/disable and VLAN-tagging changes; admin is not required. No role name beyond operator/admin/user is asserted here β€” those are the only values the provider's own documentation shows as examples. Confirm the live RBAC role catalog on the target platform before granting access, since it may differ per F5OS release.

F5OS Prerequisites

  • F5OS provider F5Networks/f5os ~> 1.10; Terraform >= 1.12.0.
  • Applies to both platform contexts β€” VELOS chassis partition and rSeries appliance. Only the expected name format convention differs between the two.
  • The named interface must already physically/logically exist on the target platform β€” this module configures an existing interface; it does not create new hardware/logical interfaces.
  • Any VLAN ID referenced via native_vlan/trunk_vlans should already exist, owned by a sibling terraform-f5os-vlan module (f5os_vlan.vlan_id) β€” this module does not create or validate VLAN existence itself.
  • The single, already-authenticated f5os provider instance in scope must target the chassis partition or appliance where the interface should be configured.

πŸ“ Module Structure

terraform-f5os-interface/
β”œβ”€β”€ providers.tf # required_version, F5Networks/f5os ~> 1.10 β€” no provider {} block
β”œβ”€β”€ variables.tf # var.interface { name, enabled, native_vlan, trunk_vlans } β€” 1:1 mapped to the real f5os_interface schema
β”œβ”€β”€ main.tf # f5os_interface.this β€” the module's single keystone resource
β”œβ”€β”€ outputs.tf # id, name, status, enabled, native_vlan, trunk_vlans
β”œβ”€β”€ SCOPE.md # lightweight standalone contract β€” RBAC, prerequisites, emits, gotchas
└── README.md # this file β€” worked example configurations live in πŸ“š Example Library below

No separate examples/ directory ships with this module β€” every worked configuration is captured inline in the πŸ“š Example Library below, using the module's real variable names.


βš™οΈ Quick Start

# The caller configures the provider once, elsewhere in the root module:
# provider "f5os" {
# host = var.f5os_host
# username = var.f5os_username
# password = var.f5os_password
# }

module "interface_1_0" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-interface.git?ref=v1.0.0"

  interface = {
    name    = "1.0"
    enabled = true
  }
}

πŸ”Œ Cross-Module Contract

Consumes

Input Type Source module
interface.native_vlan (optional) number Typically a sibling terraform-f5os-vlan module's vlan_id output β€” not Terraform-enforced; the caller wires the reference
interface.trunk_vlans (optional) set(number) Typically one or more sibling terraform-f5os-vlan modules' vlan_id outputs, collected by the caller

Emits

Output Description Consumed by
id Provider-internal unique identifier for the f5os_interface resource Informational / import use β€” not typically referenced by sibling modules
name The configured interface name, if set (null otherwise β€” see Schema notes that bite) A future terraform-f5os-lag module's members set
status Operational state of the interface, as reported by F5OS (read-only) Diagnostics/reporting
enabled Enabled/disabled state applied to this interface, as applied Diagnostics/reporting
native_vlan Native (untagged) VLAN ID applied to this interface, as applied Cross-check against terraform-f5os-vlan's vlan_id output
trunk_vlans Set of trunk (tagged) VLAN IDs applied to this interface, as applied Cross-check against terraform-f5os-vlan's vlan_id output

πŸ“š Example Library

1 Β· Minimal interface

The smallest real call β€” always set name even though the schema marks it Optional (see "Schema notes that bite").

module "interface_minimal" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-interface.git?ref=v1.0.0"

  interface = {
    name = "1.0"
  }
}

πŸ’‘ With only name set, enabled is left to F5OS's own computed value (Optional+Computed), and no VLAN tagging is applied.

2 Β· Fully specified interface
module "interface_full" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-interface.git?ref=v1.0.0"

  interface = {
    name        = "1.2"
    enabled     = true
    native_vlan = 100
    trunk_vlans = [200, 201, 202]
  }
}
3 Β· VELOS chassis partition blade/port naming
# provider "f5os" {
# host = var.velos_partition_mgmt_ip
# username = var.f5os_username
# password = var.f5os_password
# }

module "interface_velos" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-interface.git?ref=v1.0.0"

  interface = {
    name    = "1/1.0"
    enabled = true
  }
}

ℹ️ VELOS chassis partitions require the blade/port format (blade/port, e.g. 1/1.0) β€” the blade number identifies the physical slot within the chassis partition.

4 Β· rSeries appliance port-only naming
# provider "f5os" {
# host = var.rseries_appliance_mgmt_ip
# username = var.f5os_username
# password = var.f5os_password
# }

module "interface_rseries" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-interface.git?ref=v1.0.0"

  interface = {
    name    = "1.0"
    enabled = true
  }
}

πŸ’‘ f5os_interface behaves identically on VELOS and rSeries β€” there is no platform-context branch inside this module; only the name format convention differs, and that's a caller concern (which provider instance / which platform they're targeting).

5 Β· Disabling an interface
module "interface_disabled" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-interface.git?ref=v1.0.0"

  interface = {
    name    = "1.3"
    enabled = false
  }
}

⚠️ Disabling an interface that is currently carrying production traffic will interrupt that traffic β€” confirm the interface is unused, or that its traffic has already failed over, before applying.

6 Β· Native VLAN only (no trunk)
module "interface_native_only" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-interface.git?ref=v1.0.0"

  interface = {
    name        = "1.4"
    enabled     = true
    native_vlan = 50
  }
}
7 Β· Trunk VLANs only (no native)
module "interface_trunk_only" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-interface.git?ref=v1.0.0"

  interface = {
    name        = "1.5"
    enabled     = true
    trunk_vlans = [300, 301]
  }
}
8 Β· Omitting name (failure mode, documented deliberately)
module "interface_no_name" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-interface.git?ref=v1.0.0"

  interface = {
    enabled = true
    # name intentionally omitted
  }
}

⚠️ This passes terraform validate (the type is optional(string), matching the provider's own schema) but leaves the resource with no way to identify which interface to configure β€” expect an apply-time failure, or an ambiguous applied change, against a real chassis/appliance. Set interface.name explicitly instead. See "Schema notes that bite."

9 Β· Multiple interfaces via root-module for_each

This module manages exactly one interface per call β€” fan-out across several interfaces is a root-module concern.

locals {
  interfaces = {
    port_1_0 = { name = "1.0", enabled = true, native_vlan = 100 }
    port_1_1 = { name = "1.1", enabled = true, trunk_vlans = [200, 201] }
    port_1_2 = { name = "1.2", enabled = false }
  }
}

module "interfaces" {
  source   = "git::https://github.com/microsoftexpert/terraform-f5os-interface.git?ref=v1.0.0"
  for_each = local.interfaces

  interface = each.value
}
10 Β· Referencing a sibling VLAN module's vlan_id
module "vlan_app_data" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-vlan.git?ref=v1.0.0"

  vlan = {
    vlan_id = 400
    name    = "vlan400-app-data"
  }
}

module "interface_1_6" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-interface.git?ref=v1.0.0"

  interface = {
    name        = "1.6"
    enabled     = true
    native_vlan = module.vlan_app_data.vlan_id
  }
}

πŸ”— Reference vlan_id, not id β€” F5OS cross-references VLANs by numeric ID. This module does not enforce the reference as a hard Terraform dependency; the caller wires it via a plain number, so always pass module.vlan_app_data.vlan_id rather than a hardcoded literal.

11 Β· Preparing an interface for LAG membership (no VLANs attached)
module "interface_lag_member" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-interface.git?ref=v1.0.0"

  interface = {
    name    = "1.7"
    enabled = true
    # native_vlan / trunk_vlans intentionally omitted
  }
}

⚠️ F5OS documents that LAG member interfaces "shouldn't have any VLANs attached." If this interface's name output will later be collected into a terraform-f5os-lag module's members set, leave native_vlan/trunk_vlans null here and apply VLAN tagging at the LAG level instead.

12 Β· Externalizing input via terraform.tfvars
# interface.auto.tfvars
interface = {
  name        = "1.8"
  enabled     = true
  native_vlan = 500
}
# main.tf (caller)
module "interface_from_tfvars" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-interface.git?ref=v1.0.0"

  interface = var.interface
}
13 Β· Importing an existing interface
module "interface_existing" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-interface.git?ref=v1.0.0"

  interface = {
    name    = "1.0"
    enabled = true
  }
}
terraform import module.interface_existing.f5os_interface.this 1.0

πŸ”’ Per the provider's documented import syntax, the bare interface name is the import key β€” not a composite string.

πŸ—οΈ 14 Β· End-to-end composition

A VLAN provisioned once, then referenced by an interface deployed into a VELOS chassis partition, alongside a tenant that also references the same VLAN β€” illustrating the full cross-module contract this module participates in.

module "vlan_app_data" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-vlan.git?ref=v1.0.0"

  vlan = {
    vlan_id = 600
    name    = "vlan600-app-data"
  }
}

module "partition_appA" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-partition.git?ref=v1.0.0"

  # Illustrative β€” consult terraform-f5os-partition's own README for its full variable schema.
}

module "interface_1_1" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-interface.git?ref=v1.0.0"

  interface = {
    name        = "1/1.1"
    enabled     = true
    native_vlan = module.vlan_app_data.vlan_id
  }
}

module "tenant_appA" {
  source = "git::https://github.com/microsoftexpert/terraform-f5os-tenant.git?ref=v1.0.0"

  # Illustrative β€” consult terraform-f5os-tenant's own README.
  # vlans = [module.vlan_app_data.vlan_id]
  # partition_name = module.partition_appA.name # VELOS only
}

πŸ’‘ Why it matters: this is the shape every real deployment takes β€” VLAN and chassis partition provisioned independently and in parallel, then referenced by ID/name from the interface and tenant modules that depend on them. Never hardcode a literal VLAN ID or interface name downstream; always pass module.vlan_app_data.vlan_id / module.interface_1_1.name.


πŸ“₯ Inputs

Name Type Required Description
interface object({ name = optional(string), enabled = optional(bool), native_vlan = optional(number), trunk_vlans = optional(set(number)) }) Yes (the wrapping object itself has no default; every field inside is individually schema-optional) Interface configuration, 1:1 mapped to the f5os_interface provider schema.
Full interface object schema
variable "interface" {
  type = object({
    name        = optional(string)      # Optional per schema; required in practice to identify the interface.
    enabled     = optional(bool)        # Optional+Computed β€” left null, F5OS supplies its own current value.
    native_vlan = optional(number)      # Optional. Untagged VLAN ID. No provider-side default.
    trunk_vlans = optional(set(number)) # Optional. Tagged VLAN ID set. No provider-side default.
  })
}

No default is invented for name beyond the provider's own null/unset behavior β€” passing it unset mirrors the provider's schema exactly rather than papering over the documented schema quirk noted in "Schema notes that bite."


🧾 Outputs

Output Description Sensitive
id Provider-internal unique identifier for the f5os_interface resource No
name The configured interface name, if set (null otherwise) No
status Operational state of the interface, as reported by F5OS (read-only) No
enabled Enabled/disabled state applied to this interface, as applied No
native_vlan Native (untagged) VLAN ID applied to this interface, as applied No
trunk_vlans Set of trunk (tagged) VLAN IDs applied to this interface, as applied No

🧠 Architecture Notes

  • Single keystone resource, no for_each. f5os_interface's schema exposes no nested/child collection (four flat arguments), so this module has no genuine child to iterate over. Multiplicity (many interfaces) is a root-module concern β€” see Example 9.
  • try(x, null) on every optional field reference in main.tf. Defensive around all four fields this module's object wraps; functionally equivalent to referencing each field directly given optional(...)'s own null-default behavior, but kept explicit per house style for optional nested-field references.
  • No VELOS-vs-rSeries branching inside the module. Unlike f5os_partition (VELOS-only) or f5os_tenant (platform-specific partition_name input), f5os_interface behaves identically on both platform contexts β€” there is nothing in this module's main.tf that varies by target. Only the name format convention (blade/port vs. port-only) differs, and that is entirely the caller's responsibility to get right for the target platform.
  • name is not enforced required by this module, despite being practically required. See "Schema notes that bite" and SCOPE.md's Design decisions β€” this mirrors the already-established house precedent set by terraform-f5os-vlan's treatment of its own optional-but-practically- required name field: validation {} is reserved for closed enums, not this class of mismatch, so the risk is documented rather than Terraform-enforced.
  • Ordering. A VLAN should exist before this module's native_vlan/trunk_vlans reference it β€” always pass a sibling terraform-f5os-vlan module's vlan_id output via an implicit Terraform reference, never a hardcoded literal. Likewise, this module's name output should exist before a future terraform-f5os-lag module collects it into a members set.
  • LAG-membership composition note. F5OS documents that LAG member interfaces "shouldn't have any VLANs attached." When this module's output feeds a LAG's members set, leave native_vlan/trunk_vlans null here and apply VLAN tagging at the LAG level instead.

🧱 Design Principles

Concern Secure default Opt-out (caller must type extra)
Interface identification (name) No module-invented default β€” always caller-supplied. This module documents (rather than forces via nullable = false) that omitting name leaves the resource unable to identify which interface to configure, per catalog precedent (terraform-f5os-vlan) Caller explicitly sets interface.name (strongly recommended in every real deployment)
VLAN tagging (native_vlan/trunk_vlans) No default "allow all VLANs" β€” both fields left null unless the caller explicitly lists VLAN IDs, matching the house-wide "no allow-all VLANs" rule Caller explicitly sets native_vlan and/or trunk_vlans
Enabled state (enabled) Left to the provider's own Optional+Computed default rather than the module forcing true or false Caller explicitly sets interface.enabled per their compliance/performance requirements
Credentials / secrets Not applicable β€” f5os_interface carries no secret-shaped argument N/A

πŸš€ Runbook

cd C:\GitHubCode\newf5modules\f5os\terraform-f5os-interface
terraform init -backend=false
terraform validate
terraform fmt -check
Remove-Item -Recurse -Force.terraform,.terraform.lock.hcl -ErrorAction SilentlyContinue

Pin every consumer to a specific release: ?ref=v1.0.0.


πŸ§ͺ Testing

This is a plan-only proof gate β€” it never touches a real chassis or appliance.

Check Catches Does not catch
terraform validate Wrong type on any field, malformed HCL, missing wrapping interface object The name-optional-but-practically-required quirk; whether the named interface actually exists on the target platform; RBAC/permission failures
terraform fmt -check Formatting drift from canonical style Anything semantic
terraform plan (human-run, against real infra) The above two gaps β€” a real F5OS structured error surfaces at plan/apply time β€”

πŸ’¬ Example Output

$ terraform output
enabled = true
id = "1.2"
name = "1.2"
native_vlan = 100
status = "up"
trunk_vlans = [
 200,
 201,
 202,
]

πŸ” Troubleshooting

Symptom Cause Fix
apply fails, or applies an ambiguous change, with no clear target interface name omitted β€” schema marks it Optional but the resource has no way to identify which interface to configure without it Set interface.name explicitly (see Example 1 / Example 8)
apply fails with an "interface not found"-style error Named interface doesn't physically/logically exist on the target platform, or the format doesn't match the platform context Use blade/port format (e.g. 1/1.0) on a VELOS chassis partition, port-only format (e.g. 1.0) on an rSeries appliance
apply fails on a VLAN-tagging error native_vlan/trunk_vlans references a VLAN ID that doesn't exist on the target platform yet Ensure the referenced terraform-f5os-vlan module is applied first, and double-check the VLAN ID
Permission denied / RBAC error at apply The authenticated user's role lacks interface-management privilege on the target chassis partition or appliance Grant at least the operator role scoped to that partition/appliance
init cannot resolve provider version ~> 1.10 no longer matches the current Terraform Registry listing Re-verify the pin against the registry and update providers.tf
Plan shows unexpected diff on enabled with no caller change enabled left null (Optional+Computed) and F5OS's own reported current value changed out-of-band Reconcile via terraform plan -refresh-only, or set interface.enabled explicitly to pin the desired state

πŸ”— Related Docs


πŸ’™ "Infrastructure as Code should be standardized, consistent, and secure."

Releases

Packages

Contributors

Languages