feat(deps): update terraform unifi ( 0.55.0 → 0.58.0 ) - #2668
Open
renovate[bot] wants to merge 1 commit into
Open
renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
renovate
Bot
force-pushed
the
renovate/unifi-0.x
branch
from
September 24, 2026 20:39
a1bf4ec to
0ae39d9
Compare
renovate
Bot
force-pushed
the
renovate/unifi-0.x
branch
from
September 29, 2026 14:44
0ae39d9 to
0fe126a
Compare
renovate
Bot
force-pushed
the
renovate/unifi-0.x
branch
from
October 3, 2026 09:59
0fe126a to
a116d3e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
0.55.0→0.58.0Release Notes
ubiquiti-community/terraform-provider-unifi (unifi)
v0.58.0Compare Source
✨ Features
unifi_setting: manage Global Switch Settings via the newglobal_switchblock (stp_version,dhcp_snoop,jumboframe_enabled,dot1x_portctrl_enabled). Current controllers only honor jumbo frames at the site level: settingunifi_device.jumboframe_enabled = trueis accepted and then read back asfalse, failing apply withProvider produced inconsistent result after apply, so jumbo frames were previously unmanageable. The block is opt-in like the others onunifi_setting, all fields are Optional+Computed withUseStateForUnknown, and only the fields you set are sent to the controller, so Global Switch options the provider doesn't model (ACL isolation, switch exclusions, link debounce, PoE staging) are left untouched (#513)unifi_firewall_policy: exposematch_macon thesourceanddestinationblocks. The controller stores amatch_macflag on every policy endpoint that makes the listedclient_macsmatch by MAC address rather than by the clients' current IP. The provider neither read nor wrote it, so a policy using MAC matching could not be expressed in Terraform and any update through the provider serializedmatch_mac: false, silently disabling it on the controller. The new attribute is Optional+Computed with afalsedefault and is round-tripped on read, so existing configurations plan clean; policies with MAC matching enabled outside Terraform show a diff until the attribute is declared. The v0 to v1 endpoint state upgrader seeds it asfalseand the next refresh overwrites it with the live value (#472)unifi_setting: manage Wireless Meshing via the newconnectivityblock (enabled,mlo_mesh_enabled,uplink_type,uplink_host). Turning meshing off on a fully wired site frees the standby mesh radio and doubles the per-band SSID budget: with meshing on, APs reserve a hidden backhaul SSID and allow 4 SSIDs per band instead of 8, so a site rebuilt from code could fail to create its fifth WLAN withreached the limit of WiFi networks per APbefore this toggle was manageable. The block is opt-in like the others onunifi_setting, every field is Optional+Computed withUseStateForUnknown, and only the fields you set are written - so the controller-generated mesh SSID and pre-shared key (x_mesh_essid,x_mesh_psk), and any option the provider does not model, are preserved across updates (#518)unifi_wan: manage PPPoE credentials viausername,passwordand the write-onlypassword_wo.unifi_wanacceptedtype = "pppoe"but had no way to set the ISP login, becausewan_usernameandx_wan_passwordwere absent from go-unifi's curated WAN payload and so never reached the controller. Both now travel, and only when set: an unset attribute keeps the key off the wire, so an imported PPPoE uplink can be managed for its other settings without clearing the login and dropping the connection.password_wo(Terraform 1.11+) is used at apply time and never written to state, so it can come from an ephemeral resource - with the trade-off that the provider then cannot detect a password changed outside Terraform, which produces no diff. Requires go-unifi ≥ v1.35.0 (ubiquiti-community/go-unifi#85) (#515)unifi_wireguard_peer: manage a peer's pre-shared key viapreshared_keyand the write-onlypreshared_key_wo. The UI offers it as a Pre-Shared Key toggle on every WireGuard client and the controller stores it, but the resource had no such attribute - and because the peer endpoints are full replaces, a key configured in the UI was silently destroyed by the next apply that touched the peer (verified on Network 10.6.106: a write omittingpreshared_keyremoves the stored key). Both attributes are now written only when set, so leaving them unset keeps the key off the wire and preserves whatever the controller holds.preshared_key_wo(Terraform 1.11+) is used at apply time and never written to state, with the trade-off that the provider cannot then detect a key changed outside Terraform. Requires go-unifi ≥ v1.35.0 (ubiquiti-community/go-unifi#86) (#490)unifi_firewall_policy: match on region via the newregionsattribute onsourceanddestination. Zone-based policies that filter by country (matching_target = "REGION") could not be expressed: neither the schema nor go-unifi carried the field, so such a policy had to be left out of Terraform entirely.matching_targetnow acceptsREGION, andregionstakes a list of two-letter country codes, round-tripped from the controller in both directions. Two controller constraints are worth knowing:REGIONis only valid on an external zone, and a write that leaves the list empty is rejected withapi.err.EmptyFirewallSourceRegions- so an imported region policy was never silently losing its country list, contrary to the original report; an apply that dropped the list failed outright instead. Requires go-unifi ≥ v1.35.0 (ubiquiti-community/go-unifi#87) (#492)unifi_setting: manage Client Device Isolation viaglobal_switch.acl_device_isolation. The "Device Isolation (ACL)" toggle under Settings > Networks is theacl_device_isolationlist on the site'sglobal_switchsetting: the network IDs whose devices may not talk to each other. It is a switch ACL, so it covers same-network traffic across access points - which neitherunifi_wlan.l2_isolation(one access point) norunifi_network.network_isolation(between networks) reaches. Previously it could only be set in the UI, so a change there produced no plan diff and drift went unnoticed. The list round-trips, an explicitly empty list clears the setting, and an unmanaged one stays off the wire so isolation configured outside Terraform survives an unrelated change to the block. Note the controller only offers this for networks routed by a UniFi gateway or L3 switch, and rejects an unsupported network rather than ignoring it (#509)unifi_site_to_site_vpn: accept a hostname aspeer_ip, and manage the IKE identifiers.peer_ipwas typed as an IPv4 address, so a tunnel whose peer is configured by hostname (a peer behind dynamic DNS, which the controller stores verbatim) could not even be refreshed:terraform importfailed withInvalid IPv4 Address String Valuebefore any configuration was compared. It is now a string accepting either an address or a hostname. The newlocal_identifier,local_identifier_enabled,remote_identifierandremote_identifier_enabledattributes expose the IKE peer-authentication identifiers the UI offers; setting an identifier enables it, as the UI does, and an explicit*_enabledflag overrides that. Unmanaged identifiers stay off the wire. Requires go-unifi ≥ v1.35.0 (ubiquiti-community/go-unifi#88), which adds the four fields to the site-vpn payload - they were absent from it, so they were never written and never cleared (#484)unifi_device:radio_table'sassisted_roaming_enabledandassisted_roaming_rssiare deprecated and no longer applied. UniFi removed 802.11k assisted roaming from the device radio table: the fields are absent from the controller API and from the firmware's own field definitions as of Network 10.6 (verified against the 10.6.106 field set, where the sibling radio fieldsmin_rssi,maxstaandsens_levelare all still present), and a UCG-Fiber on 10.6.106 reports neither key on any radio. Both attributes keep their schema entry so existing configurations still parse, but they now read back as null, are no longer sent in the device update PUT, and are no longer range-validated. Remove them from your configuration; they will be dropped in a future releaseunifi_wlan:bandsteering_modeis deprecated and no longer applied. UniFi moved band steering off the WLAN object: the field is absent from the controller's WLAN field definitions as of Network 10.6 while still present on the device, so the setting now lives onunifi_device'sbandsteering_mode. The attribute keeps its schema entry so existing configurations still parse, but it is neither read nor written and always reads back null. Set it onunifi_deviceinstead🐛 Bug Fixes
unifi_port_profile:forward = "customize"on an untagged native network now fails at plan time instead of leaving the resource tainted. The controller accepts such a profile and then storesforward = "all", so the post-apply read disagreed with the plan: Create failed withProvider produced inconsistent result after apply, the resource was marked tainted, and every later apply destroyed, recreated and failed identically - the configuration could never converge. The provider now checks the native network's VLAN id while planning and rejects the combination with the reason and both ways out (setforward = "all", or pointnative_networkconf_idat a tagged network). A lookup failure does not block the plan, and an unresolved native network is left to the apply (#496)unifi_setting: managing part of theipsorusgblock no longer disables everything else in it. Both blocks built their outgoing document from the Terraform model alone, and every boolean on go-unifi'ssettings.Ipsandsettings.Usgserializes withoutomitempty- so an attribute the configuration did not manage was written asfalse. Managing onlyips_modetherefore switched off an existing honeypot, the content-filtering blocking page, memory optimisation and torrent restriction, and theusgblock did the same across twenty flags (FTP/GRE/H.323 helpers, broadcast ping, LLDP, mDNS and the rest). Both write paths now read the live setting document first and overlay only the attributes present in configuration, so unmanaged options keep their controller values (#493)unifi_network: adopting a network by import no longer switches auto-scaling and LTE-LAN on.auto_scaleandlte_lanwere declared with a schema default oftrue, but the controller stores no default for either: a network created without the keys comes back without them, while a network written even once carries an explicit value (verified on Network 10.6.106 - a freshly created network has neither key, all eight pre-existing networks on the same site have both, set tofalse). So importing a network that had these off plannedfalse -> true, and because both values were unconditionally serialized into the update PUT,applyturned the features on - with the diff attributed to a default the user never wrote. Both attributes are nowOptional + Computedwith no default andUseStateForUnknown: left unset they stay off the wire entirely and the controller's value is preserved; set explicitly,falseincluded, they are sent as before. Requires go-unifi ≥ v1.35.0, where the two fields became tri-state pointers (ubiquiti-community/go-unifi#84). Importing an existing corporate network now reaches a strictly empty plan (#524)unifi_network: importing avlan-onlynetwork no longer plansauto_scale,internet_access,lte_lanandsetting_preferenceforever. Same root cause as #414 (fixed in #415 forgateway_type/ipv6_interface_type): the controller omits these fields for avlan-onlynetwork, and the read path copied them from the prior model, which on import carries nothing but the ID. They stayed null while the schema defaults aretrue/"auto", so every ordinary plan proposed an in-place update and import-first adoption could never reach a strict no-op -lifecycle { ignore_changes }does not suppress it, because the default is applied to the null config value after the prior state is substituted. A null or unknown prior value now resolves to the documented default; an explicitly configured value,falseincluded, is preserved. Read-side only, with no change to what is written to the controller.lte_lanhas no schemaDefault, so it falls back to thetrueits documentation promises (#517)unifi_port_profile: a configured storm-control, rate-limit or priority-queue value no longer leaves the resource tainted.egress_rate_limit_kbps,egress_rate_limit_kbps_enabled, the fourpriority_queue*_levelattributes and the tenstormctrl_*attributes were hardcoded to null/false on read, whatever the controller actually held, so any configured value failed the post-apply consistency check withProvider produced inconsistent result after apply. The profile was created on the controller and recorded in state, but Terraform marked it tainted and the next apply destroyed, recreated and failed identically - the configuration could never converge. All sixteen are now round-tripped from the controller, and an attribute the controller does not hold still reads back null rather than zero (#496)unifi_network:firewall_zone_idno longer shows-> (known after apply)on every plan. The attribute isOptional + Computedwith no plan modifier, so the framework re-planned it as unknown on any update whose configuration left it unset.ModifyPlanalready pinned it back, but only when the prior state was null - the case for controllers without zone-based firewalling. On a ZBF controller, where the controller assigns a zone on first apply, the state is always populated, so the unknown survived and made every follow-up plan non-empty. The plan is now pinned to the prior state value whenever the configuration declares none, which covers both cases. Thedhcp_relayacceptance test no longer needs its zone-based-firewall precheck as a result. The code change shipped in #526; this adds the changelog entry it was missing (#519)unifi_device: carry every configurable field into the update PUT.stp_version,stp_priority,outlet_overrides,outlet_enabled, thelcm_*display settings,poe_mode,locked,disabled,bandsteering_mode,flowctrl_enabled,jumboframe_enabled,outdoor_mode_override,volumeandx_baresip_passwordwere converted from the plan but never copied into the minimal body sent on update, so the controller kept its previous values and the post-apply read failed withProvider produced inconsistent result after apply(for examplestp_prioritystaying at32768after planning4096). All of them are now included. Every one of these fields isomitemptyin go-unifi, so attributes that are not declared stay off the wire and existing configurations are unaffected (#476, #510)TestAccDeviceFramework_radioTable. The test declared anna(5 GHz) radio on00:15:6d:00:00:01, which the compose controller simulates as a U2O - a single-band device whoseradio_tableholds only anngentry. With no matching entry to echo the controller-required radio name from, the PUT was rejected with a bareapi.err.Invalid (400)and the failure was attributed to whichever change had merged last. The simulated APs also populateradio_tableasynchronously after adoption (and often not at all), which is why the same commit passed and failed on consecutive nightly runs. The test now checks the target device actually exposes the radios it configures and skips with the reason when it does not, so radio coverage is only claimed where the hardware provides itunifi_device: apply per-device management IP and DNS changes fromconfig_network. The Terraform configuration was converted intoDeviceConfigNetworkbut dropped when assembling the minimal update payload, so the controller kept its previous values and the post-apply read could fail withProvider produced inconsistent result after apply. The update payload now includes the configured management network settings, includingdns1anddns2; an unset block stays omitted (#482)v0.57.0Compare Source
✨ Features
unifi_firewall_policy: expose the "Match Opposite" (invert) toggles. Newmatch_opposite_ips,match_opposite_networksandmatch_opposite_portsattributes on thesourceanddestinationblocks, plus a top-levelmatch_opposite_protocol, map 1:1 to the controller'smatch_opposite_*flags — the "Match Opposite" switches in the UI that make an endpoint match everything except the listed networks/IPs/ports (or protocol). Previously the provider neither read nor wrote these flags, so a policy inverted in the UI could not be expressed in Terraform and any update through the provider silently reset the inversion tofalse. All four are Optional+Computed with afalsedefault and are round-tripped from the controller on read, so existing configurations plan clean; policies inverted outside Terraform now show a diff until the attribute is declared. The v0→v1 endpoint state upgrader seeds the new fields asfalse(refresh overwrites them with the live values)🐛 Bug Fixes
unifi_network: collapse duplicate DHCP NTP servers returned by the controller. UniFi can store a single configured server in both DHCP NTP fields, causingdhcp_server.ntp_serversto read back with duplicate entries and produce inconsistent-result errors or persistent drift. Both the resource and data source now return unique servers in their original order; the resource continues to preserve the distinction between an unset list and an explicitly empty list (#477)attribute "site" is required. v0.56.0 addedsite(andnetwork_idonunifi_wireguard_peer) to the resource identity of 21 resources without bumping the identity schema version. Terraform decodes the old{"id"}identity with the new attributes null, but OpenTofu requires every attribute of the current identity schema to be present, sounifi_dns_record,unifi_firewall_group,unifi_network,unifi_port_forwardandunifi_wanfailed directly and their dependents were skipped. The identity schema ofap_group,client_qos_rate,dns_record,firewall_group,firewall_policy,firewall_rule,firewall_zone,network,port_forward,port_profile,power_supervisor,radius_profile,radius_user,site_to_site_vpn,static_route,traffic_route,vpn_client,vpn_server,wan,wireguard_peerandwlanis now at version 1, with a shared upgrader that carries stored values over and fills a missingsitefrom the provider's configured site. No configuration or state changes are needed. A resource whosesitediffers from the provider's site gets the provider's site in its upgraded identity. Aunifi_wireguard_peeridentity written before v0.56.0 keeps a nullnetwork_id, since an identity upgrader cannot see state; plans are unaffected because Read takesnetwork_idfrom state (#506)unifi_port_profile: an explicitly emptyport_security_mac_addressno longer fails apply withProvider produced inconsistent result after apply. The controller stores a profile whose Port State is Disabled asport_security_enabled = truewith an empty MAC allowlist, so no device can pass. The read turned an empty allowlist from the controller intonull, so a configured[]could not be kept in state and both create and update failed, although the controller had accepted the write. An explicitly empty set now reads back as[], an unset attribute still reads asnull, and addresses the controller reports are adopted as before. To disable ports through a profile, setport_security_enabled = truewithport_security_mac_address = []:forward = "disabled"alone leaves the port Active (#470)unifi_firewall_policy: fixProvider produced inconsistent result after applywhen UniFi renumbers a policy'sindex. The controller can renumber remaining policies when siblings are deleted during the same apply. The computed, read-onlyindexno longer usesUseStateForUnknown, allowing an updated policy's post-apply read to refresh the controller-assigned value instead of comparing it against a stale index pinned in the plan (#348, #473)v0.56.1Compare Source
🐛 Bug Fixes
unifi_device: fixterraform planfailing after upgrading from v0.55.0 withfailed to decode identity: unsupported attribute "id". v0.56.0 changed theunifi_deviceresource identity key from the controller deviceidto the devicemacwithout bumping the identity schema version, so every state holding aunifi_devicewritten by v0.55.0 or earlier failed to plan. The identity schema is now at version 1 with an upgrader for version 0 identities, which exist in two shapes: a{"mac"}identity written by v0.56.0 is carried over, and an{"id"}identity written by earlier versions is rebuilt from the state'smacattribute on the next refresh. No configuration or state changes are needed, and states already written by v0.56.0 keep working. Other resources whose identity only gained an optionalsite(ornetwork_id) attribute in v0.56.0 were not affected (#502, #504)v0.56.0Compare Source
✨ Features
unifi_wlan: per-SSID band steering via the newbandsteering_modeattribute (off|equal|prefer_5g). Modern controllers expose band steering on the wlanconf record, and on WiFi 6/7 access points this per-SSID control has replaced the legacy device-level one (still available asunifi_device.bandsteering_mode), so band steering was previously unmanageable on that hardware. Optional+Computed with value validation; the value is echoed from the controller on read, stays entirely off the wire when unset, and on controllers without per-SSID band steering (which accept and ignore the key) the declared value is kept in state instead of failing the apply with an inconsistent-result error. Requires go-unifi withWLAN.BandsteeringMode(go-unifi#72) (#388)🐛 Bug Fixes
unifi_network:dhcp_server.dns_servers,dhcp_server.ntp_servers, anddhcp_server.wins.addressescan now be set back to empty. Two halves made an explicit[]impossible. Read collapsed an empty controller response tonullwhile the plan held[], failing apply with "provider produced inconsistent result after apply"; the readback now mirrors the previous value's null-ness, so[]round-trips as[]and never-configured staysnull. And on the wire, go-unifi taggeddhcpd_dns_1..4withomitemptyand squashed a pointer-to-emptydhcpd_ntp_1..2to nil, so the clearing PUT silently omitted the fields and the controller (whose networkconf PUT keeps omitted fields) retained the old servers — fixed upstream in go-unifi#73, picked up by the go.mod bump. Because corporate/guest updates now serialize those fields unconditionally, updates on networks whose configuration does not manage thedhcp_serverblock preserve the controller's current DHCP options (range, lease time, DNS/NTP/WINS, boot fields) by reading the network first, the same pattern #439 introduced fordhcp_guarding(#429)unifi_wlan: creating a WLAN with"6g"inwlan_bandsno longer fails withProvider produced inconsistent result after apply. The provider marshals the full declared band list —6gincluded — on both create and update, but some controllers (observed on Network 10.4.x) silently drop6gfrom the initial create response while accepting the identical payload on a subsequent update; the reporter's manual workaround (create without6g, then add it) confirmed the asymmetry.Createnow compares the controller's response against the requested band list and, when a band was dropped, immediately re-asserts the intended configuration with a single follow-up update — the created WLAN carries all declared bands in oneterraform apply. If the controller refuses the band even then, the apply now fails with an actionable diagnostic (6GHz gating: WPA3/SAE with PMF, 6GHz-capable APs) instead of the cryptic consistency error, and the created WLAN is tracked in state as tainted rather than orphaned on the controller (#406)unifi_device: fix declaredradio_tablerejecting every update withapi.err.InvalidPayload(400). A radio_table entry declared with only some sub-fields (e.g.radio/channel/ht/tx_power_mode) leaves the rest Unknown — not Null — in the plan, andValueInt64Pointer()maps Unknown to a pointer at the Go zero value, so every undeclared numeric sub-field went out as a literal0("antenna_gain":0,"min_rssi":0,"sens_level":0,…), which the controller rejects; and simply omitting them is not enough, because the controller requires each entry to carry the hardware-assigned radionameand fails withapi.err.MissingValue(keyradio) without it. Unknown sub-fields now stay off the wire like Null, hardware-derived fields the practitioner left unset (name,antenna_gain,antenna_id, channel/width/power when undeclared) are echoed from the device's current radio table so controller-required values are always present, and the post-apply state keeps the declared list's count/order/values, resolving only the undeclared sub-fields from the controller. As part of the same diff-hygiene class, an emptyport_overridesnow mirrors the device's existing representation (null vs[]) instead of always sending[], which manufactured a spuriousnull→[]change that some controllers reject on devices without ports (#427)unifi_setting.ips:suppression_alerts/suppression_whitelistare now actually saved on current controllers instead of producing a perpetual diff. Newer controllers (observed on Network 10.4.x) no longer store IPS suppression nested inside theipssetting: theset/setting/ipsendpoint silently drops the nestedsuppressionfield and the data lives under the standaloneips_suppressionsetting key, so the provider's writes never landed and every plan re-showed the whole list. The provider now re-reads theipssetting after writing it; when the controller did not persist the nested field, it writes the entries to theips_suppressionsetting (and refresh reads them back from there). Controllers that still nest suppression underipskeep using the nested path unchanged, and controllers that support neither shape now fail loudly instead of drifting silently (#381)unifi_setting.usg:geo_ip_filtering_*now actually configures Region Blocking on current controllers instead of applying without effect. Newer controllers (observed on Network 10.4.x) ignore thegeo_ip_filtering_*fields on theusgsetting: the live Region Blocking configuration is stored under the standaloneusg_geosetting key (asip_filtering.{action,countries,enabled,traffic_direction}), so applies never changed anything and OpenTofu/Terraform reported an inconsistent result. The provider now also writes the configured fields tousg_geo(mappinggeo_ip_filtering_blocktoaction), treats a storedusg_geosetting as authoritative on refresh, and keeps existing configurations working unchanged on older controllers that reject theusg_geokey and persist the fields onusgitself — no configuration changes needed on either controller generation (#374)unifi_network: stopip_aliasesandnat_outbound_ip_addressesalways failing apply with "provider produced inconsistent result after apply".Readhardcoded both attributes tonullregardless of what the API returned, so any network with a non-empty value configured failed every apply, unconditionally. Both now round-trip from the controller's response.ipv6_aliasesremains unsupported — go-unifi'sNetworkstruct has no field for it even though the controller accepts and returns it — but a configured value is now rejected with a clear error at plan time instead of failing apply with a confusing inconsistent-result error (#413)unifi_client: fix every in-place update failing withProvider produced inconsistent result after apply: .last_ip.last_ipandhostnameare values the controller reports from live observation, not derived from any configured attribute, yet both usedUseStateForUnknown. That plan modifier pins the planned value to the prior state whenever config doesn't set the attribute — appropriate for values that only change when the practitioner changes them, but wrong here, since the controller can legitimately report a new value between plan and apply (the client re-associates, a DHCP lease renews, etc.). Terraform then requires the applied value to exactly match the pinned plan value, so any update — regardless of which attribute actually changed — failed oncelast_iporhostnamedrifted. Both are now plainComputedattributes that plan as(known after apply)on update, so the real post-apply value from the controller is always accepted (#428)unifi_network: makedhcp_guardingactually take effect oncorporate/guest/vlan-onlynetworks. Two independent drops hiddhcp_guardingfrom the controller. First, go-unifi historically only marshaleddhcpd_ip_1..3forpurpose = "vlan-only"networks — fixed upstream (go-unifi#68) and already picked up by the go-unifi bump on main, which serializes them forcorporateandguesttoo. Second — and still present until now — the provider defaultssetting_preferencetoauto, and the controller force-resetsdhcpguard_enabledtofalseon any write to an auto-managed network, so guarding either silently never took effect or apply failed withProvider produced inconsistent result after apply: .dhcp_guarding.enabled.ModifyPlannow pinssetting_preferencetomanualwheneverdhcp_guarding.enabledis plannedtrueon acorporate/guestnetwork (vlan-only networks keep guarding underauto) and the practitioner has not setsetting_preferenceexplicitly, mirroring the existing DHCP relay behavior; an explicitsetting_preference = "auto"combined with enabled guarding now gets a plan-time warning explaining the controller will reset it. Also fixes the perpetual non-empty plan this pinning previously caused for DHCP relay networks on controllers without zone-based firewalling: thesetting_preferencedefault flappingauto→manualmade every plan an update, which re-planned the nullfirewall_zone_idas(known after apply)forever;ModifyPlannow pins the plan back to null when neither state nor config carry a zone (#419)unifi_network: stop updates silently disabling DHCP guarding configured outside Terraform. The update PUT is assembled from the Terraform model alone, so when thedhcp_guardingblock is absent from configuration the body carriesdhcpguard_enabled: false(with the trusted-server fields empty or, for corporate/guest networks on current go-unifi, absent from the wire entirely) — any unrelated update (changing the DHCP pool, DNS, anything) wiped guarding that was enabled on the controller, with no plan diff and no error. When the block is unmanaged the provider now reads the network first and carries the controller's current guarding fields through the update; removing the block therefore preserves guarding rather than disabling it — disable explicitly withenabled = false. Managingdhcp_guardingend-to-end additionally requires go-unifi to marshaldhcpd_ip_1..3for corporate/guest networks (go-unifi#68) — without that fix the controller rejects an enabled guard withapi.err.MissingIPAddress(#439)unifi_device: pin down and guard the fix that stops zero declaredport_overrideblocks wiping live switch overrides. A switch with live controller-side overrides, updated while config declares zeroport_overrideblocks (e.g. a name-only change), must send the controller's current overrides unchanged — notport_overrides: null(rejected by some controllers withapi.err.InvalidPayload, 400) and not[](which would silently reset every live override, #436 is only safe when the device truly has none). This behavior already existed inupdateDevicebut was untested and duplicated across anif/else; it is now a singleresolvePortOverridesForUpdatehelper with direct regression coverage (#438)unifi_device: stop a declaredport_overridestripping settings from every port on the device.op_modeis only written when it is not"switch"(gateways reject it, #213) andDevicePortOverrides.OpModeisomitempty, so declaring a port that carriesop_mode: "switch"on the controller produced an entry with the field missing.UpdateDevicePUTsgetDeviceDiff(existing, target)and comparesport_overridesas a single JSON value, so that one difference sent the whole array — full-replaced from a copy round-tripped throughDevicePortOverrides, which drops the fields it does not model (stp_edge_state,stp_bpdu_guard_enabled,multicast_router_mode,sd_wan_underlay_port) and every field at its zero value. Undeclared ports were stripped along with declared ones, the plan showed nothing, and the apply reported success. The merge now carries a controller-sideop_modeforward onto the declared entry, so the marshalled array stays identical and nothing is written; a config-supplied non-switchop_modestill wins, and no value is ever sourced from config, so #213 stays fixed (#430, #266, #213)unifi_setting.ntp: stop empty NTP server slots causing perpetual diffs or inconsistent results. The controller stores unusedntp_server_1..4values as empty strings, but the provider read them back asnull, conflicting with an explicitly configured"". The server attributes now preserve prior state during unrelated plans and normalize controller empty strings to known empty Terraform values (#382)Configuration
📅 Schedule: (in timezone America/New_York)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.