diff --git a/nsxt/resource_nsxt_edge_transport_node.go b/nsxt/resource_nsxt_edge_transport_node.go index ad7f9aace..0e3820178 100644 --- a/nsxt/resource_nsxt_edge_transport_node.go +++ b/nsxt/resource_nsxt_edge_transport_node.go @@ -97,6 +97,13 @@ func resourceNsxtEdgeTransportNode() *schema.Resource { "description": getDescriptionSchema(), "display_name": getDisplayNameSchema(), "tag": getTagsSchema(), + "node_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Unique Id of the fabric node", + ConflictsWith: []string{"ip_addresses", "fqdn", "deployment_config", "external_id"}, + }, "failure_domain": { Type: schema.TypeString, Optional: true, @@ -671,43 +678,56 @@ func getTransportNodeFromSchema(d *schema.ResourceData, m interface{}) (*mpmodel description := d.Get("description").(string) displayName := d.Get("display_name").(string) tags := getMPTagsFromSchema(d) + nodeID := d.Get("node_id").(string) failureDomain := d.Get("failure_domain").(string) hostSwitchSpec, err := getHostSwitchSpecFromSchema(d, m, nodeTypeEdge) if err != nil { return nil, fmt.Errorf("failed to create Transport Node: %v", err) } - converter := bindings.NewTypeConverter() - var dataValue data.DataValue - var errs []error + var nodeDeploymentInfo *data.StructValue + if nodeID == "" { + /* + node_id attribute conflicts with node_deployment_info. As node_deployment_info is a complex object which has + attributes with default values, this schema property will always have values - therefore there is no simple way + to enforce this conflict within the provider (e.g check if node_id and node_deployment_info have values, then + fail. + So the provider will ignore node_deployment_info properties when node_id has a value - which would mean that + this edge appliance was created externally. + */ + log.Printf("node_id not specified, will deploy edge using values in deploymentConfig") + converter := bindings.NewTypeConverter() + var dataValue data.DataValue + var errs []error - externalID := d.Get("external_id").(string) - fqdn := d.Get("fqdn").(string) - ipAddresses := interfaceListToStringList(d.Get("ip_addresses").([]interface{})) + externalID := d.Get("external_id").(string) + fqdn := d.Get("fqdn").(string) + ipAddresses := interfaceListToStringList(d.Get("ip_addresses").([]interface{})) - deploymentConfig, err := getEdgeNodeDeploymentConfigFromSchema(d.Get("deployment_config")) - if err != nil { - return nil, err - } - nodeSettings, err := getEdgeNodeSettingsFromSchema(d.Get("node_settings")) - if err != nil { - return nil, err - } - node := mpmodel.EdgeNode{ - ExternalId: &externalID, - Fqdn: &fqdn, - IpAddresses: ipAddresses, - DeploymentConfig: deploymentConfig, - NodeSettings: nodeSettings, - ResourceType: mpmodel.EdgeNode__TYPE_IDENTIFIER, - } - dataValue, errs = converter.ConvertToVapi(node, mpmodel.EdgeNodeBindingType()) + deploymentConfig, err := getEdgeNodeDeploymentConfigFromSchema(d.Get("deployment_config")) + if err != nil { + return nil, err + } + nodeSettings, err := getEdgeNodeSettingsFromSchema(d.Get("node_settings")) + if err != nil { + return nil, err + } + node := mpmodel.EdgeNode{ + ExternalId: &externalID, + Fqdn: &fqdn, + IpAddresses: ipAddresses, + DeploymentConfig: deploymentConfig, + NodeSettings: nodeSettings, + ResourceType: mpmodel.EdgeNode__TYPE_IDENTIFIER, + } + dataValue, errs = converter.ConvertToVapi(node, mpmodel.EdgeNodeBindingType()) - if errs != nil { - log.Printf("Failed to convert node object, errors are %v", errs) - return nil, errs[0] + if errs != nil { + log.Printf("Failed to convert node object, errors are %v", errs) + return nil, errs[0] + } + nodeDeploymentInfo = dataValue.(*data.StructValue) } - nodeDeploymentInfo := dataValue.(*data.StructValue) obj := mpmodel.TransportNode{ Description: &description, @@ -725,6 +745,29 @@ func resourceNsxtEdgeTransportNodeCreate(d *schema.ResourceData, m interface{}) connector := getPolicyConnector(m) client := nsx.NewTransportNodesClient(connector) + nodeID := d.Get("node_id").(string) + if nodeID != "" { + obj, err := client.Get(nodeID) + if err != nil { + return handleCreateError("TransportNode", nodeID, err) + } + // Set node_id, revision and computed values in schema + d.Set("failure_domain", obj.FailureDomainId) + + converter := bindings.NewTypeConverter() + base, errs := converter.ConvertToGolang(obj.NodeDeploymentInfo, mpmodel.EdgeNodeBindingType()) + if errs != nil { + return handleCreateError("TransportNode", nodeID, errs[0]) + } + node := base.(mpmodel.EdgeNode) + d.Set("external_id", node.ExternalId) + d.Set("fqdn", node.Fqdn) + d.Set("ip_addresses", node.IpAddresses) + + d.SetId(nodeID) + return resourceNsxtEdgeTransportNodeUpdate(d, m) + } + obj, err := getTransportNodeFromSchema(d, m) if err != nil { return err @@ -1236,6 +1279,7 @@ func resourceNsxtEdgeTransportNodeRead(d *schema.ResourceData, m interface{}) er d.Set("description", obj.Description) d.Set("display_name", obj.DisplayName) setMPTagsInSchema(d, obj.Tags) + d.Set("node_id", obj.NodeId) d.Set("failure_domain", obj.FailureDomainId) if obj.HostSwitchSpec != nil { diff --git a/website/docs/d/transport_node_realization.html.markdown b/website/docs/d/transport_node_realization.html.markdown index 9785be490..32322f526 100644 --- a/website/docs/d/transport_node_realization.html.markdown +++ b/website/docs/d/transport_node_realization.html.markdown @@ -45,7 +45,7 @@ resource "nsxt_transport_node" "test" { } } node_settings { - hostname = "tf_edge_node" + hostname = "tf-edge-node" allow_ssh_root_login = true enable_ssh = true } diff --git a/website/docs/r/edge_transport_node.html.markdown b/website/docs/r/edge_transport_node.html.markdown index b462feb09..cf0d8b043 100644 --- a/website/docs/r/edge_transport_node.html.markdown +++ b/website/docs/r/edge_transport_node.html.markdown @@ -46,7 +46,7 @@ resource "nsxt_edge_transport_node" "test" { } } node_settings { - hostname = "tf_edge_node" + hostname = "tf-edge-node" allow_ssh_root_login = true enable_ssh = true } @@ -56,6 +56,38 @@ resource "nsxt_edge_transport_node" "test" { **NOTE:** `data.vsphere_network`, `data.vsphere_compute_cluster`, `data.vsphere_datastore`, `data.vsphere_host` are obtained using [hashicorp/vsphere](https://registry.terraform.io/providers/hashicorp/vsphere/) provider. +## Example Usage, with Edge Transport Node created externally and converted into a transport node using Terraform +```hcl +data "nsxt_transport_node" "test_node" { + display_name = "tf_edge_node" +} + +resource "nsxt_edge_transport_node" "test_node" { + node_id = data.nsxt_transport_node.test_node.id + description = "Terraform-deployed edge node" + display_name = "tf_edge_node" + standard_host_switch { + ip_assignment { + static_ip_pool = data.nsxt_policy_ip_pool.vtep_ip_pool.realized_id + } + transport_zone_endpoint { + transport_zone = data.nsxt_transport_zone.overlay_tz.id + } + transport_zone_endpoint { + transport_zone = data.nsxt_transport_zone.vlan_tz.id + } + host_switch_profile = [data.nsxt_policy_uplink_host_switch_profile.edge_uplink_profile.path] + pnic { + device_name = "fp-eth0" + uplink_name = "uplink1" + } + } + node_settings { + hostname = "tf-edge-node" + } +} +``` + ## Argument Reference The following arguments are supported: @@ -63,6 +95,7 @@ The following arguments are supported: * `display_name` - (Required) Display name of the resource. * `description` - (Optional) Description of the resource. * `tag` - (Optional) A list of scope + tag pairs to associate with this resource. +* `node_id` - (Optional) The id of a pre-deployed Edge appliance to be converted into a transport node. Note that `node_id` attribute conflicts with `external_id`, `fqdn`, `ip_addresses` `deployment_config` and `node_settings` and those will be ignored while specifying `node_id`. * `failure_domain` - (Optional) Id of the failure domain. * `standard_host_switch` - (Required) Standard host switch specification. * `host_switch_id` - (Optional) The host switch id. This ID will be used to reference a host switch.