Manages a single physical/logical network interface (
f5os_interface) on an F5OS platform β VELOS chassis partition or rSeries appliance β targeting theF5Networks/f5osprovider~> 1.10(verified against v1.12.0).
- π·οΈ Manages exactly one
f5os_interfaceresource β 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, nofor_eachinside the module boundary. - π Applies identically to a VELOS chassis partition or an rSeries appliance β the
f5os_interfaceschema carries no platform-specific argument; only the expectednameformat 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-vlanmodule owns, and emitsnamefor a futureterraform-f5os-lagmodule 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/plantime instead of an opaque failure against a real chassis or appliance.
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!
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;
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.
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;
Resource inventory
| Resource | Terraform address | Cardinality |
|---|---|---|
f5os_interface |
f5os_interface.this |
Exactly 1 per module call |
| 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
requiredin the live schema βnameincluded. This is very likely a schema oversight rather than intentional design: the closely related sibling resourcef5os_lagmarks its ownnameargument Required. Terraform's type system cannot catch this on its own sincenameis validly typed asoptional(string)here β omitting it will passterraform validateand leave the resource unable to identify which interface to configure, very likely failing (or applying ambiguously) atapplytime against a real chassis or appliance. Always setinterface.nameexplicitly. enabledis 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_vlanandtrunk_vlansare Optional (not computed) with no provider-side default β left null, no native VLAN or trunk VLANs are configured.trunk_vlansis aSet 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. idandstatusare Read-Only/computed β never settable inputs.
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 provider
F5Networks/f5os~> 1.10; Terraform>= 1.12.0. - Applies to both platform contexts β VELOS chassis partition and rSeries appliance. Only the
expected
nameformat 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_vlansshould already exist, owned by a siblingterraform-f5os-vlanmodule (f5os_vlan.vlan_id) β this module does not create or validate VLAN existence itself. - The single, already-authenticated
f5osprovider instance in scope must target the chassis partition or appliance where the interface should be configured.
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.
# 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
}
}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 |
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
nameset,enabledis 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_interfacebehaves identically on VELOS and rSeries β there is no platform-context branch inside this module; only thenameformat 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 passesterraform validate(the type isoptional(string), matching the provider's own schema) but leaves the resource with no way to identify which interface to configure β expect anapply-time failure, or an ambiguous applied change, against a real chassis/appliance. Setinterface.nameexplicitly 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, notidβ 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 passmodule.vlan_app_data.vlan_idrather 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'snameoutput will later be collected into aterraform-f5os-lagmodule'smembersset, leavenative_vlan/trunk_vlansnull 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.
| 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."
| 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 |
- 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 inmain.tf. Defensive around all four fields this module's object wraps; functionally equivalent to referencing each field directly givenoptional(...)'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) orf5os_tenant(platform-specificpartition_nameinput),f5os_interfacebehaves identically on both platform contexts β there is nothing in this module'smain.tfthat varies by target. Only thenameformat convention (blade/port vs. port-only) differs, and that is entirely the caller's responsibility to get right for the target platform. nameis 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 byterraform-f5os-vlan's treatment of its own optional-but-practically- requirednamefield: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_vlansreference it β always pass a siblingterraform-f5os-vlanmodule'svlan_idoutput via an implicit Terraform reference, never a hardcoded literal. Likewise, this module'snameoutput should exist before a futureterraform-f5os-lagmodule collects it into amembersset. - 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
membersset, leavenative_vlan/trunk_vlansnull here and apply VLAN tagging at the LAG level instead.
| 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 |
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 SilentlyContinuePin every consumer to a specific release: ?ref=v1.0.0.
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 | β |
$ terraform output
enabled = true
id = "1.2"
name = "1.2"
native_vlan = 100
status = "up"
trunk_vlans = [
200,
201,
202,
]
| 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 |
f5os_interfaceresource reference β F5Networks/f5os provider docsf5os_lagresource reference β cross-check source for thename-required schema quirk noted abovef5os_userresource reference β source of the confirmed RBAC role examples (admin,operator,user)- Sibling modules:
terraform-f5os-vlan,terraform-f5os-lag,terraform-f5os-tenant,terraform-f5os-partition - This module's
SCOPE.md
π "Infrastructure as Code should be standardized, consistent, and secure."