diff --git a/docs/modules/metal_virtual_circuit.md b/docs/modules/metal_virtual_circuit.md
index 4016e5a..23e472a 100644
--- a/docs/modules/metal_virtual_circuit.md
+++ b/docs/modules/metal_virtual_circuit.md
@@ -45,16 +45,16 @@ Manage a Virtual Circuit in Equinix Metal. You can use *id* or *name* to lookup
| `port_id` |
`str` | Optional | UUID of the Connection Port where the VC is scoped to. |
| `nni_vlan` | `int` | Optional | Equinix Metal network-to-network VLAN ID. |
| `vlan_id` | `str` | Optional | UUID of the VLAN to associate. |
-| `vnid` | `str` | Optional | VNID VLAN parameter, see the documentation for Equinix Fabric. |
-| `description` | `str` | Optional | Description for the Virtual Circuit resource. |
-| `tags` | `str` | Optional | Tags for the Virtual Circuit resource. |
-| `speed` | `str` | Optional | Speed of the Virtual Circuit resource. |
+| `vnid` | `str` | Optional | VNID VLAN parameter, see the documentation for Equinix Fabric. **(Updatable)** |
+| `description` | `str` | Optional | Description for the Virtual Circuit resource. **(Updatable)** |
+| `tags` | `str` | Optional | Tags for the Virtual Circuit resource. **(Updatable)** |
+| `speed` | `str` | Optional | Speed of the Virtual Circuit resource. **(Updatable)** |
| `vrf` | `str` | Optional | UUID of the VRF to associate. |
-| `peer_asn` | `int` | Optional | The BGP ASN of the peer. The same ASN may be the used across several VCs, but it cannot be the same as the local_asn of the VRF. |
-| `subnet` | `str` | Optional | A subnet from one of the IP blocks associated with the VRF that we will help create an IP reservation for. Can only be either a /30 or /31. For a /31 block, it will only have two IP addresses, which will be used for the metal_ip and customer_ip. For a /30 block, it will have four IP addresses, but the first and last IP addresses are not usable. We will default to the first usable IP address for the metal_ip. |
-| `metal_ip` | `str` | Optional | The Metal IP address for the SVI (Switch Virtual Interface) of the VirtualCircuit. Will default to the first usable IP in the subnet. |
-| `customer_ip` | `str` | Optional | The Customer IP address which the CSR switch will peer with. Will default to the other usable IP in the subnet. |
-| `md5` | `str` | Optional | The password that can be set for the VRF BGP peer |
+| `peer_asn` | `int` | Optional | The BGP ASN of the peer. The same ASN may be the used across several VCs, but it cannot be the same as the local_asn of the VRF. **(Updatable)** |
+| `subnet` | `str` | Optional | A subnet from one of the IP blocks associated with the VRF that we will help create an IP reservation for. Can only be either a /30 or /31. For a /31 block, it will only have two IP addresses, which will be used for the metal_ip and customer_ip. For a /30 block, it will have four IP addresses, but the first and last IP addresses are not usable. We will default to the first usable IP address for the metal_ip. **(Updatable)** |
+| `metal_ip` | `str` | Optional | The Metal IP address for the SVI (Switch Virtual Interface) of the VirtualCircuit. Will default to the first usable IP in the subnet. **(Updatable)** |
+| `customer_ip` | `str` | Optional | The Customer IP address which the CSR switch will peer with. Will default to the other usable IP in the subnet. **(Updatable)** |
+| `md5` | `str` | Optional | The password that can be set for the VRF BGP peer **(Updatable)** |
| `timeout` | `int` | Optional | Timeout in seconds for Virtual Circuit to get to "ready" state **(Default: `15`)** |
diff --git a/plugins/module_utils/equinix.py b/plugins/module_utils/equinix.py
index eb4bf8a..9b2218c 100644
--- a/plugins/module_utils/equinix.py
+++ b/plugins/module_utils/equinix.py
@@ -252,9 +252,20 @@ def only_defined_mutable(params: dict, mutables: list):
return return_dict
-def get_diff(params: dict, fetched: dict, mutables: list):
+def get_diff(params: dict, fetched: dict, mutables: list, overwrite_undefined_from_api=False):
current_mutable = only_defined_mutable(params, mutables)
- fetched_mutable = only_defined_mutable(fetched, mutables)
+
+ # The shared get_diff function will not report a diff if an attribute
+ # has a None value in either the config or the API response. Setting
+ # overwrite_undefined_from_api to True tells get_diff to report a diff
+ # if an attribute is defined in the config but not the API response.
+ # TODO: Long-term we should probably move away from this centralized
+ # logic so that each module has to make its own decisions about when
+ # and how to update attributes
+ if overwrite_undefined_from_api:
+ fetched_mutable = fetched
+ else:
+ fetched_mutable = only_defined_mutable(fetched, mutables)
defined_mutable_keys = current_mutable.keys() & fetched_mutable.keys()
if defined_mutable_keys == set():
diff --git a/plugins/module_utils/metal/metal_api.py b/plugins/module_utils/metal/metal_api.py
index acef0f0..34cf56e 100644
--- a/plugins/module_utils/metal/metal_api.py
+++ b/plugins/module_utils/metal/metal_api.py
@@ -349,6 +349,7 @@ def private_ipv4_subnet_size(resource: dict):
'id': 'id',
'name': 'name',
'customer_ip': optional('customer_ip'),
+ 'md5': optional('md5'),
'metal_ip': optional('metal_ip'),
'nni_vlan': 'nni_vlan',
'peer_asn': optional('peer_asn'),
diff --git a/plugins/modules/metal_virtual_circuit.py b/plugins/modules/metal_virtual_circuit.py
index 775e640..dacb023 100644
--- a/plugins/modules/metal_virtual_circuit.py
+++ b/plugins/modules/metal_virtual_circuit.py
@@ -231,24 +231,28 @@
description=[
'VNID VLAN parameter, see the documentation for Equinix Fabric.'
],
+ editable=True
),
description=SpecField(
type=FieldType.string,
description=[
'Description for the Virtual Circuit resource.'
],
+ editable=True,
),
tags=SpecField(
type=FieldType.string,
description=[
'Tags for the Virtual Circuit resource.'
],
+ editable=True
),
speed=SpecField(
type=FieldType.string,
description=[
'Speed of the Virtual Circuit resource.'
],
+ editable=True
),
vrf=SpecField(
type=FieldType.string,
@@ -262,6 +266,7 @@
'The BGP ASN of the peer.',
'The same ASN may be the used across several VCs, but it cannot be the same as the local_asn of the VRF.'
],
+ editable=True,
),
subnet=SpecField(
type=FieldType.string,
@@ -272,6 +277,7 @@
'For a /30 block, it will have four IP addresses, but the first and last IP addresses are not usable.',
'We will default to the first usable IP address for the metal_ip.',
],
+ editable=True,
),
metal_ip=SpecField(
type=FieldType.string,
@@ -279,6 +285,7 @@
'The Metal IP address for the SVI (Switch Virtual Interface) of the VirtualCircuit.',
'Will default to the first usable IP in the subnet.'
],
+ editable=True,
),
customer_ip=SpecField(
type=FieldType.string,
@@ -286,12 +293,14 @@
'The Customer IP address which the CSR switch will peer with.',
'Will default to the other usable IP in the subnet.'
],
+ editable=True,
),
md5=SpecField(
type=FieldType.string,
description=[
'The password that can be set for the VRF BGP peer'
],
+ editable=True,
),
timeout=SpecField(
type=FieldType.integer,
@@ -405,7 +414,8 @@ def main():
if fetched:
module.params['id'] = fetched['id']
if state == "present":
- diff = get_diff(module.params, fetched, MUTABLE_ATTRIBUTES)
+ overwrite_undefined_from_api = True
+ diff = get_diff(module.params, fetched, MUTABLE_ATTRIBUTES, overwrite_undefined_from_api)
if diff:
fetched = module.update_by_id(diff, module_route)
changed = True