diff --git a/vsphere/helper_test.go b/vsphere/helper_test.go index a23b22b3b..5a4e26299 100644 --- a/vsphere/helper_test.go +++ b/vsphere/helper_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/terraform" "github.com/vmware/govmomi" + "github.com/vmware/govmomi/vim25/types" ) // testCheckVariables bundles common variables needed by various test checkers. @@ -49,3 +50,23 @@ func testAccSkipIfNotEsxi(t *testing.T) { t.Skip("set VSPHERE_TEST_ESXI to run ESXi-specific acceptance tests") } } + +// testGetPortGroup is a convenience method to fetch a static port group +// resource for testing. +func testGetPortGroup(s *terraform.State, resourceName string) (*types.HostPortGroup, error) { + tVars, err := testClientVariablesForResource(s, fmt.Sprintf("vsphere_host_port_group.%s", resourceName)) + if err != nil { + return nil, err + } + + hsID, name, err := splitHostPortGroupID(tVars.resourceID) + if err != nil { + return nil, err + } + ns, err := hostNetworkSystemFromHostSystemID(tVars.client, hsID) + if err != nil { + return nil, fmt.Errorf("error loading host network system: %s", err) + } + + return hostPortGroupFromName(tVars.client, ns, name) +} diff --git a/vsphere/host_network_system_helper.go b/vsphere/host_network_system_helper.go index 09fef7891..b4f63505f 100644 --- a/vsphere/host_network_system_helper.go +++ b/vsphere/host_network_system_helper.go @@ -28,8 +28,8 @@ func hostNetworkSystemFromHostSystemID(client *govmomi.Client, hsID string) (*ob return hostNetworkSystemFromHostSystem(hs) } -// hostVSwitchFromName locates a virtual switch on the supplied HostSystem by -// name. +// hostVSwitchFromName locates a virtual switch on the supplied +// HostNetworkSystem by name. func hostVSwitchFromName(client *govmomi.Client, ns *object.HostNetworkSystem, name string) (*types.HostVirtualSwitch, error) { var mns mo.HostNetworkSystem pc := client.PropertyCollector() @@ -47,3 +47,23 @@ func hostVSwitchFromName(client *govmomi.Client, ns *object.HostNetworkSystem, n return nil, fmt.Errorf("could not find virtual switch %s", name) } + +// hostPortGroupFromName locates a port group on the supplied HostNetworkSystem +// by name. +func hostPortGroupFromName(client *govmomi.Client, ns *object.HostNetworkSystem, name string) (*types.HostPortGroup, error) { + var mns mo.HostNetworkSystem + pc := client.PropertyCollector() + ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout) + defer cancel() + if err := pc.RetrieveOne(ctx, ns.Reference(), []string{"networkInfo.portgroup"}, &mns); err != nil { + return nil, fmt.Errorf("error fetching host network properties: %s", err) + } + + for _, pg := range mns.NetworkInfo.Portgroup { + if pg.Spec.Name == name { + return &pg, nil + } + } + + return nil, fmt.Errorf("could not find port group %s", name) +} diff --git a/vsphere/host_port_group_structure.go b/vsphere/host_port_group_structure.go new file mode 100644 index 000000000..c7b075348 --- /dev/null +++ b/vsphere/host_port_group_structure.go @@ -0,0 +1,143 @@ +package vsphere + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "github.com/hashicorp/terraform/terraform" + "github.com/vmware/govmomi/vim25/types" +) + +const hostPortGroupIDPrefix = "tf-HostPortGroup" + +// schemaHostPortGroupSpec returns schema items for resources that +// need to work with HostPortGroupSpec, such as port groups. +func schemaHostPortGroupSpec() map[string]*schema.Schema { + s := map[string]*schema.Schema{ + // HostPortGroupSpec + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "The name of the port group.", + ForceNew: true, + }, + "vlan_id": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Description: "The VLAN ID/trunk mode for this port group. An ID of 0 denotes no tagging, an ID of 1-4094 tags with the specific ID, and an ID of 4095 enables trunk mode, allowing the guest to manage its own tagging.", + Default: 0, + ValidateFunc: validation.IntBetween(0, 4095), + }, + "virtual_switch_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "The name of the virtual switch to bind this port group to.", + ForceNew: true, + }, + } + mergeSchema(s, schemaHostNetworkPolicy()) + return s +} + +// expandHostPortGroupSpec reads certain ResourceData keys and returns a +// HostPortGroupSpec. +func expandHostPortGroupSpec(d *schema.ResourceData) *types.HostPortGroupSpec { + obj := &types.HostPortGroupSpec{ + Name: d.Get("name").(string), + VlanId: int32(d.Get("vlan_id").(int)), + VswitchName: d.Get("virtual_switch_name").(string), + Policy: *expandHostNetworkPolicy(d), + } + return obj +} + +// flattenHostPortGroupSpec reads various fields from a HostPortGroupSpec into +// the passed in ResourceData. +func flattenHostPortGroupSpec(d *schema.ResourceData, obj *types.HostPortGroupSpec) error { + d.Set("vlan_id", obj.VlanId) + if err := flattenHostNetworkPolicy(d, &obj.Policy); err != nil { + return err + } + return nil +} + +// calculateComputedPolicy is a utility function to compute a map of state +// attributes for the port group's effective policy. It uses a bit of a +// roundabout way to set the attributes, but allows us to utilize our +// functional deep reading helpers to perform this task, versus having to +// re-write code. +// +// This function relies a bit on some of the lower-level utility functionality +// of helper/schema, so it may need to change in the future. +func calculateComputedPolicy(policy types.HostNetworkPolicy) (map[string]string, error) { + cpr := &schema.Resource{Schema: schemaHostNetworkPolicy()} + cpd := cpr.Data(&terraform.InstanceState{}) + cpd.SetId("effectivepolicy") + if err := flattenHostNetworkPolicy(cpd, &policy); err != nil { + return nil, fmt.Errorf("error setting effective policy data: %s", err) + } + cpm := cpd.State().Attributes + delete(cpm, "id") + return cpm, nil +} + +// calculatePorts is a utility function that returns a set of port data. +func calculatePorts(ports []types.HostPortGroupPort) *schema.Set { + s := make([]interface{}, 0) + for _, port := range ports { + m := make(map[string]interface{}) + m["key"] = port.Key + m["mac_addresses"] = sliceStringsToInterfaces(port.Mac) + m["type"] = port.Type + s = append(s, m) + } + return schema.NewSet(schema.HashResource(portGroupPortSchema()), s) +} + +// portGroupPortSchema returns a sub-schema for a port group's connected ports. +func portGroupPortSchema() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": &schema.Schema{ + Type: schema.TypeString, + Description: "The linkable identifier for this port entry.", + Computed: true, + }, + "mac_addresses": &schema.Schema{ + Type: schema.TypeList, + Description: "The MAC addresses of the network service of the virtual machine connected on this port.", + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "type": &schema.Schema{ + Type: schema.TypeString, + Description: "Type type of the entity connected on this port. Possible values are host (VMKkernel), systemManagement (service console), virtualMachine, or unknown.", + Computed: true, + }, + }, + } +} + +// saveHostPortGroupID sets a special ID for a host virtual switch, composed of +// the MOID for the concerned HostSystem and the port group's key. +func saveHostPortGroupID(d *schema.ResourceData, hsID, name string) { + d.SetId(fmt.Sprintf("%s:%s:%s", hostPortGroupIDPrefix, hsID, name)) +} + +// splitHostPortGroupID splits a vsphere_host_port_group resource ID into its +// counterparts: the prefix, the HostSystem ID, and the port group name. +func splitHostPortGroupID(raw string) (string, string, error) { + s := strings.SplitN(raw, ":", 3) + if len(s) != 3 || s[0] != hostPortGroupIDPrefix || s[1] == "" || s[2] == "" { + return "", "", fmt.Errorf("corrupt ID: %s", raw) + } + return s[1], s[2], nil +} + +// portGroupIDsFromResourceID passes a resource's ID through +// splitHostPortGroupID. +func portGroupIDsFromResourceID(d *schema.ResourceData) (string, string, error) { + return splitHostPortGroupID(d.Id()) +} diff --git a/vsphere/provider.go b/vsphere/provider.go index 9a824882a..5534fb932 100644 --- a/vsphere/provider.go +++ b/vsphere/provider.go @@ -72,6 +72,7 @@ func Provider() terraform.ResourceProvider { "vsphere_datacenter": resourceVSphereDatacenter(), "vsphere_file": resourceVSphereFile(), "vsphere_folder": resourceVSphereFolder(), + "vsphere_host_port_group": resourceVSphereHostPortGroup(), "vsphere_host_virtual_switch": resourceVSphereHostVirtualSwitch(), "vsphere_license": resourceVSphereLicense(), "vsphere_virtual_disk": resourceVSphereVirtualDisk(), diff --git a/vsphere/resource_vsphere_host_port_group.go b/vsphere/resource_vsphere_host_port_group.go new file mode 100644 index 000000000..214fd7039 --- /dev/null +++ b/vsphere/resource_vsphere_host_port_group.go @@ -0,0 +1,148 @@ +package vsphere + +import ( + "fmt" + + "context" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/vmware/govmomi" +) + +func resourceVSphereHostPortGroup() *schema.Resource { + s := map[string]*schema.Schema{ + "host_system_id": &schema.Schema{ + Type: schema.TypeString, + Description: "The managed object ID of the host to set the virtual switch up on.", + Required: true, + ForceNew: true, + }, + "computed_policy": &schema.Schema{ + Type: schema.TypeMap, + Description: "The effective network policy after inheritance. Note that this will look similar to, but is not the same, as the policy attributes defined in this resource.", + Computed: true, + }, + "key": &schema.Schema{ + Type: schema.TypeString, + Description: "The linkable identifier for this port group.", + Computed: true, + }, + "ports": &schema.Schema{ + Type: schema.TypeSet, + Description: "The ports that currently exist and are used on this port group.", + Computed: true, + MaxItems: 1, + Elem: portGroupPortSchema(), + }, + } + mergeSchema(s, schemaHostPortGroupSpec()) + + // Transform any necessary fields in the schema that need to be updated + // specifically for this resource. + s["active_nics"].Optional = true + s["standby_nics"].Optional = true + + return &schema.Resource{ + Create: resourceVSphereHostPortGroupCreate, + Read: resourceVSphereHostPortGroupRead, + Update: resourceVSphereHostPortGroupUpdate, + Delete: resourceVSphereHostPortGroupDelete, + Schema: s, + } +} + +func resourceVSphereHostPortGroupCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*govmomi.Client) + name := d.Get("name").(string) + hsID := d.Get("host_system_id").(string) + ns, err := hostNetworkSystemFromHostSystemID(client, hsID) + if err != nil { + return fmt.Errorf("error loading network system: %s", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout) + defer cancel() + spec := expandHostPortGroupSpec(d) + if err := ns.AddPortGroup(ctx, *spec); err != nil { + return fmt.Errorf("error adding port group: %s", err) + } + + saveHostPortGroupID(d, hsID, name) + return resourceVSphereHostPortGroupRead(d, meta) +} + +func resourceVSphereHostPortGroupRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*govmomi.Client) + hsID, name, err := portGroupIDsFromResourceID(d) + if err != nil { + return err + } + ns, err := hostNetworkSystemFromHostSystemID(client, hsID) + if err != nil { + return fmt.Errorf("error loading host network system: %s", err) + } + + pg, err := hostPortGroupFromName(meta.(*govmomi.Client), ns, name) + if err != nil { + return fmt.Errorf("error fetching port group data: %s", err) + } + + if err := flattenHostPortGroupSpec(d, &pg.Spec); err != nil { + return fmt.Errorf("error setting resource data: %s", err) + } + + d.Set("key", pg.Key) + cpm, err := calculateComputedPolicy(pg.ComputedPolicy) + if err != nil { + return err + } + if err := d.Set("computed_policy", cpm); err != nil { + return fmt.Errorf("error saving effective policy to state: %s", err) + } + if err := d.Set("ports", calculatePorts(pg.Port)); err != nil { + return fmt.Errorf("error setting port list: %s", err) + } + + return nil +} + +func resourceVSphereHostPortGroupUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*govmomi.Client) + hsID, name, err := portGroupIDsFromResourceID(d) + if err != nil { + return err + } + ns, err := hostNetworkSystemFromHostSystemID(client, hsID) + if err != nil { + return fmt.Errorf("error loading host network system: %s", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout) + defer cancel() + spec := expandHostPortGroupSpec(d) + if err := ns.UpdatePortGroup(ctx, name, *spec); err != nil { + return fmt.Errorf("error updating port group: %s", err) + } + + return resourceVSphereHostPortGroupRead(d, meta) +} + +func resourceVSphereHostPortGroupDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*govmomi.Client) + hsID, name, err := portGroupIDsFromResourceID(d) + if err != nil { + return err + } + ns, err := hostNetworkSystemFromHostSystemID(client, hsID) + if err != nil { + return fmt.Errorf("error loading host network system: %s", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout) + defer cancel() + if err := ns.RemovePortGroup(ctx, name); err != nil { + return fmt.Errorf("error deleting port group: %s", err) + } + + return nil +} diff --git a/vsphere/resource_vsphere_host_port_group_test.go b/vsphere/resource_vsphere_host_port_group_test.go new file mode 100644 index 000000000..f84eb8e82 --- /dev/null +++ b/vsphere/resource_vsphere_host_port_group_test.go @@ -0,0 +1,267 @@ +package vsphere + +import ( + "fmt" + "os" + "reflect" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccResourceVSphereHostPortGroup(t *testing.T) { + var tp *testing.T + testAccResourceVSphereHostPortGroupCases := []struct { + name string + testCase resource.TestCase + }{ + { + "basic, inherited policy", + resource.TestCase{ + PreCheck: func() { + testAccPreCheck(tp) + testAccResourceVSphereHostPortGroupPreCheck(tp) + }, + Providers: testAccProviders, + CheckDestroy: testAccResourceVSphereHostPortGroupExists(false), + Steps: []resource.TestStep{ + { + Config: testAccResourceVSphereHostPortGroupConfig(), + Check: resource.ComposeTestCheckFunc( + testAccResourceVSphereHostPortGroupExists(true), + ), + }, + }, + }, + }, + { + "more complex configuration and overridden attributes", + resource.TestCase{ + PreCheck: func() { + testAccPreCheck(tp) + testAccResourceVSphereHostPortGroupPreCheck(tp) + }, + Providers: testAccProviders, + CheckDestroy: testAccResourceVSphereHostPortGroupExists(false), + Steps: []resource.TestStep{ + { + Config: testAccResourceVSphereHostPortGroupConfigWithOverrides(), + Check: resource.ComposeTestCheckFunc( + testAccResourceVSphereHostPortGroupExists(true), + testAccResourceVSphereHostPortGroupCheckVlan(1000), + testAccResourceVSphereHostPortGroupCheckEffectiveActive([]string{os.Getenv("VSPHERE_HOST_NIC0")}), + testAccResourceVSphereHostPortGroupCheckEffectiveStandby([]string{os.Getenv("VSPHERE_HOST_NIC1")}), + testAccResourceVSphereHostPortGroupCheckEffectivePromisc(true), + ), + }, + }, + }, + }, + { + "basic, then complex config", + resource.TestCase{ + PreCheck: func() { + testAccPreCheck(tp) + testAccResourceVSphereHostPortGroupPreCheck(tp) + }, + Providers: testAccProviders, + CheckDestroy: testAccResourceVSphereHostPortGroupExists(false), + Steps: []resource.TestStep{ + { + Config: testAccResourceVSphereHostPortGroupConfig(), + Check: resource.ComposeTestCheckFunc( + testAccResourceVSphereHostPortGroupExists(true), + ), + }, + { + Config: testAccResourceVSphereHostPortGroupConfigWithOverrides(), + Check: resource.ComposeTestCheckFunc( + testAccResourceVSphereHostPortGroupExists(true), + testAccResourceVSphereHostPortGroupCheckVlan(1000), + testAccResourceVSphereHostPortGroupCheckEffectiveActive([]string{os.Getenv("VSPHERE_HOST_NIC0")}), + testAccResourceVSphereHostPortGroupCheckEffectiveStandby([]string{os.Getenv("VSPHERE_HOST_NIC1")}), + testAccResourceVSphereHostPortGroupCheckEffectivePromisc(true), + ), + }, + }, + }, + }, + } + + for _, tc := range testAccResourceVSphereHostPortGroupCases { + t.Run(tc.name, func(t *testing.T) { + tp = t + resource.Test(t, tc.testCase) + }) + } +} + +func testAccResourceVSphereHostPortGroupPreCheck(t *testing.T) { + if os.Getenv("VSPHERE_HOST_NIC0") == "" { + t.Skip("set VSPHERE_HOST_NIC0 to run vsphere_host_port_group acceptance tests") + } + if os.Getenv("VSPHERE_HOST_NIC1") == "" { + t.Skip("set VSPHERE_HOST_NIC1 to run vsphere_host_port_group acceptance tests") + } + if os.Getenv("VSPHERE_ESXI_HOST") == "" { + t.Skip("set VSPHERE_ESXI_HOST to run vsphere_host_port_group acceptance tests") + } +} + +func testAccResourceVSphereHostPortGroupExists(expected bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + name := "PGTerraformTest" + id := "pg" + _, err := testGetPortGroup(s, id) + if err != nil { + if err.Error() == fmt.Sprintf("could not find port group %s", name) && expected == false { + // Expected missing + return nil + } + return err + } + if expected == false { + return fmt.Errorf("expected port group %s to still be missing", name) + } + return nil + } +} + +func testAccResourceVSphereHostPortGroupCheckVlan(expected int32) resource.TestCheckFunc { + return func(s *terraform.State) error { + id := "pg" + pg, err := testGetPortGroup(s, id) + if err != nil { + return err + } + actual := pg.Spec.VlanId + if expected != actual { + return fmt.Errorf("expected VLAN ID to be %d, got %d", expected, actual) + } + return nil + } +} + +func testAccResourceVSphereHostPortGroupCheckEffectiveActive(expected []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + id := "pg" + pg, err := testGetPortGroup(s, id) + if err != nil { + return err + } + actual := pg.ComputedPolicy.NicTeaming.NicOrder.ActiveNic + if !reflect.DeepEqual(expected, actual) { + return fmt.Errorf("expected effective active NICs to be %#v, got %#v", expected, actual) + } + return nil + } +} + +func testAccResourceVSphereHostPortGroupCheckEffectiveStandby(expected []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + id := "pg" + pg, err := testGetPortGroup(s, id) + if err != nil { + return err + } + actual := pg.ComputedPolicy.NicTeaming.NicOrder.StandbyNic + if !reflect.DeepEqual(expected, actual) { + return fmt.Errorf("expected effective standby NICs to be %#v, got %#v", expected, actual) + } + return nil + } +} + +func testAccResourceVSphereHostPortGroupCheckEffectivePromisc(expected bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + id := "pg" + pg, err := testGetPortGroup(s, id) + if err != nil { + return err + } + actual := *pg.ComputedPolicy.Security.AllowPromiscuous + if expected != actual { + return fmt.Errorf("expected effective allow promiscuous to be %t, got %t", expected, actual) + } + return nil + } +} + +func testAccResourceVSphereHostPortGroupConfig() string { + return fmt.Sprintf(` +variable "host_nic0" { + default = "%s" +} + +variable "host_nic1" { + default = "%s" +} + +data "vsphere_datacenter" "datacenter" { + name = "%s" +} + +data "vsphere_host" "esxi_host" { + name = "%s" + datacenter_id = "${data.vsphere_datacenter.datacenter.id}" +} + +resource "vsphere_host_virtual_switch" "switch" { + name = "vSwitchTerraformTest" + host_system_id = "${data.vsphere_host.esxi_host.id}" + + network_adapters = ["${var.host_nic0}", "${var.host_nic1}"] + active_nics = ["${var.host_nic0}", "${var.host_nic1}"] + standby_nics = [] +} + +resource "vsphere_host_port_group" "pg" { + name = "PGTerraformTest" + host_system_id = "${data.vsphere_host.esxi_host.id}" + virtual_switch_name = "${vsphere_host_virtual_switch.switch.name}" +} +`, os.Getenv("VSPHERE_HOST_NIC0"), os.Getenv("VSPHERE_HOST_NIC1"), os.Getenv("VSPHERE_DATACENTER"), os.Getenv("VSPHERE_ESXI_HOST")) +} + +func testAccResourceVSphereHostPortGroupConfigWithOverrides() string { + return fmt.Sprintf(` +variable "host_nic0" { + default = "%s" +} + +variable "host_nic1" { + default = "%s" +} + +data "vsphere_datacenter" "datacenter" { + name = "%s" +} + +data "vsphere_host" "esxi_host" { + name = "%s" + datacenter_id = "${data.vsphere_datacenter.datacenter.id}" +} + +resource "vsphere_host_virtual_switch" "switch" { + name = "vSwitchTerraformTest" + host_system_id = "${data.vsphere_host.esxi_host.id}" + + network_adapters = ["${var.host_nic0}", "${var.host_nic1}"] + active_nics = ["${var.host_nic0}", "${var.host_nic1}"] + standby_nics = [] + allow_promiscuous = false +} + +resource "vsphere_host_port_group" "pg" { + name = "PGTerraformTest" + host_system_id = "${data.vsphere_host.esxi_host.id}" + virtual_switch_name = "${vsphere_host_virtual_switch.switch.name}" + + vlan_id = 1000 + active_nics = ["${var.host_nic0}"] + standby_nics = ["${var.host_nic1}"] + allow_promiscuous = true +} +`, os.Getenv("VSPHERE_HOST_NIC0"), os.Getenv("VSPHERE_HOST_NIC1"), os.Getenv("VSPHERE_DATACENTER"), os.Getenv("VSPHERE_ESXI_HOST")) +} diff --git a/website/docs/r/host_port_group.html.markdown b/website/docs/r/host_port_group.html.markdown new file mode 100644 index 000000000..0247d8d33 --- /dev/null +++ b/website/docs/r/host_port_group.html.markdown @@ -0,0 +1,128 @@ +--- +layout: "vsphere" +page_title: "VMware vSphere: vsphere_host_port_group" +sidebar_current: "docs-vsphere-resource-host-port-group" +description: |- + Provides a vSphere Host Port Group Resource. This can be used to configure port groups direct on an ESXi host. +--- + +# vsphere\_host\_port\_group + +The `vsphere_host_port_group` resource can be used to manage vSphere standard +port groups on an ESXi host. These port groups are connected to standard +virtual switches, which can be managed by the +[`vsphere_host_virtual_switch`][host-virtual-switch] resource. + +For an overview on vSphere networking concepts, see [this page][ref-vsphere-net-concepts]. + +[host-virtual-switch]: /docs/providers/vsphere/r/host_virtual_switch.html +[ref-vsphere-net-concepts]: https://docs.vmware.com/en/VMware-vSphere/6.5/com.vmware.vsphere.networking.doc/GUID-2B11DBB8-CB3C-4AFF-8885-EFEA0FC562F4.html + +## Example Usages + +**Create a virtual switch and bind a port group to it:** + +```hcl +data "vsphere_datacenter" "datacenter" { + name = "dc1" +} + +data "vsphere_host" "esxi_host" { + name = "esxi1" + datacenter_id = "${data.vsphere_datacenter.datacenter.id}" +} + +resource "vsphere_host_virtual_switch" "switch" { + name = "vSwitchTerraformTest" + host_system_id = "${data.vsphere_host.esxi_host.id}" + + network_adapters = ["vmnic0", "vmnic1"] + + active_nics = ["vmnic0"] + standby_nics = ["vmnic1"] +} + +resource "vsphere_host_port_group" "pg" { + name = "PGTerraformTest" + host_system_id = "${data.vsphere_host.esxi_host.id}" + virtual_switch_name = "${vsphere_host_virtual_switch.switch.name}" +} +``` + +**Create a port group with VLAN set and some overrides:** + +This example sets the trunk mode VLAN (`4095`, which passes through all tags) +and sets +[`allow_promiscuous`](/docs/providers/vsphere/r/host_virtual_switch.html#allow_promiscuous) +to ensure that all traffic is seen on the port. The latter setting overrides +the implicit default of `false` set on the virtual switch. + +```hcl +data "vsphere_datacenter" "datacenter" { + name = "dc1" +} + +data "vsphere_host" "esxi_host" { + name = "esxi1" + datacenter_id = "${data.vsphere_datacenter.datacenter.id}" +} + +resource "vsphere_host_virtual_switch" "switch" { + name = "vSwitchTerraformTest" + host_system_id = "${data.vsphere_host.esxi_host.id}" + + network_adapters = ["vmnic0", "vmnic1"] + + active_nics = ["vmnic0"] + standby_nics = ["vmnic1"] +} + +resource "vsphere_host_port_group" "pg" { + name = "PGTerraformTest" + host_system_id = "${data.vsphere_host.esxi_host.id}" + virtual_switch_name = "${vsphere_host_virtual_switch.switch.name}" + + vlan_id = 4095 + + allow_promiscuous = true +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (String, required, forces new resource) The name of the port group. +* `host_system_id` - (String, required, forces new resource) The managed object + ID of the host to set the port group up on. +* `virtual_switch_name` - (String, required, forces new resource) The name of + the virtual switch to bind this port group to. +* `vlan_id` - (Integer, optional) The VLAN ID/trunk mode for this port group. + An ID of `0` denotes no tagging, an ID of `1`-`4094` tags with the specific ID, and + an ID of `4095` enables trunk mode, allowing the guest to manage its own + tagging. Default: `0`. + +### Policy Options + +In addition to the above options, you can configure any policy option that is +available under the [`vsphere_host_virtual_switch` policy options +section][host-vswitch-policy-options]. Any policy option that is not set is +**inherited** from the virtual switch, its options propagating to the port +group. + +See the link for a full list of options that can be set. + +[host-vswitch-policy-options]: /docs/providers/vsphere/r/host_virtual_switch.html#policy-options + +## Attribute Reference + +The following attributes are exported: + +* `id` - An ID unique to Terraform for this port group. The convention is a + prefix, the host system ID, and the port group name. An example would be + `tf-HostPortGroup:host-10:PGTerraformTest`. +* `comptued_policy` - A map with a full set of the [policy + options][host-vswitch-policy-options] computed from defaults and overrides, + explaining the effective policy for this port group. +* `key` - The key for this port group as returned from the vSphere API. +* `ports` - A list of ports that currently exist and are used on this port group. diff --git a/website/vsphere.erb b/website/vsphere.erb index e4776c041..cf3fc801e 100644 --- a/website/vsphere.erb +++ b/website/vsphere.erb @@ -34,6 +34,9 @@ > vsphere_folder + > + vsphere_host_port_group + > vsphere_host_virtual_switch