diff --git a/api/api_list.yaml b/api/api_list.yaml index 80cc82ba8..e56339ed2 100644 --- a/api/api_list.yaml +++ b/api/api_list.yaml @@ -5,8 +5,10 @@ # - client: ### API model path # model: -### API type (Local/Global/Multitenancy) +### API type (Local/Global/Multitenancy/VPC) # type: +### Attributes to be ignored while implementing a method +# ignore_params: ### List results Model path # list_result_model: ### Name of model within model path package (should be same in all implementations) @@ -242,6 +244,13 @@ - client: github.com/vmware/vsphere-automation-sdk-go/services/nsxt/orgs/projects/infra/domains model: github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model type: Multitenancy + - client: github.com/vmware/vsphere-automation-sdk-go/services/nsxt/orgs/projects/vpcs + model: github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model + type: VPC + ignore_params: + Delete: + - failIfSubtreeExistsParam + - forceParam model_name: Group obj_name: Group supported_method: diff --git a/api/infra/domains/group.go b/api/infra/domains/group.go index 6fad70aa3..dd1bff036 100644 --- a/api/infra/domains/group.go +++ b/api/infra/domains/group.go @@ -11,6 +11,7 @@ import ( client0 "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra/domains" model0 "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model" client2 "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/orgs/projects/infra/domains" + client3 "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/orgs/projects/vpcs" utl "github.com/vmware/terraform-provider-nsxt/api/utl" ) @@ -31,6 +32,9 @@ func NewGroupsClient(sessionContext utl.SessionContext, connector vapiProtocolCl case utl.Multitenancy: client = client2.NewGroupsClient(connector) + case utl.VPC: + client = client3.NewGroupsClient(connector) + default: return nil } @@ -67,6 +71,13 @@ func (c GroupClientContext) Get(domainIdParam string, groupIdParam string) (mode return obj, err } + case utl.VPC: + client := c.Client.(client3.GroupsClient) + obj, err = client.Get(utl.DefaultOrgID, c.ProjectID, c.VPCID, groupIdParam) + if err != nil { + return obj, err + } + default: return obj, errors.New("invalid infrastructure for model") } @@ -94,6 +105,10 @@ func (c GroupClientContext) Patch(domainIdParam string, groupIdParam string, gro client := c.Client.(client2.GroupsClient) err = client.Patch(utl.DefaultOrgID, c.ProjectID, domainIdParam, groupIdParam, groupParam) + case utl.VPC: + client := c.Client.(client3.GroupsClient) + err = client.Patch(utl.DefaultOrgID, c.ProjectID, c.VPCID, groupIdParam, groupParam) + default: err = errors.New("invalid infrastructure for model") } @@ -130,6 +145,10 @@ func (c GroupClientContext) Update(domainIdParam string, groupIdParam string, gr client := c.Client.(client2.GroupsClient) obj, err = client.Update(utl.DefaultOrgID, c.ProjectID, domainIdParam, groupIdParam, groupParam) + case utl.VPC: + client := c.Client.(client3.GroupsClient) + obj, err = client.Update(utl.DefaultOrgID, c.ProjectID, c.VPCID, groupIdParam, groupParam) + default: err = errors.New("invalid infrastructure for model") } @@ -153,6 +172,10 @@ func (c GroupClientContext) Delete(domainIdParam string, groupIdParam string, fa client := c.Client.(client2.GroupsClient) err = client.Delete(utl.DefaultOrgID, c.ProjectID, domainIdParam, groupIdParam, failIfSubtreeExistsParam, forceParam) + case utl.VPC: + client := c.Client.(client3.GroupsClient) + err = client.Delete(utl.DefaultOrgID, c.ProjectID, c.VPCID, groupIdParam) + default: err = errors.New("invalid infrastructure for model") } @@ -185,6 +208,10 @@ func (c GroupClientContext) List(domainIdParam string, cursorParam *string, incl client := c.Client.(client2.GroupsClient) obj, err = client.List(utl.DefaultOrgID, c.ProjectID, domainIdParam, cursorParam, includeMarkForDeleteObjectsParam, includedFieldsParam, memberTypesParam, pageSizeParam, sortAscendingParam, sortByParam) + case utl.VPC: + client := c.Client.(client3.GroupsClient) + obj, err = client.List(utl.DefaultOrgID, c.ProjectID, c.VPCID, cursorParam, includeMarkForDeleteObjectsParam, includedFieldsParam, memberTypesParam, pageSizeParam, sortAscendingParam, sortByParam) + default: err = errors.New("invalid infrastructure for model") } diff --git a/nsxt/provider.go b/nsxt/provider.go index c239f2ba4..219c61f93 100644 --- a/nsxt/provider.go +++ b/nsxt/provider.go @@ -497,6 +497,7 @@ func Provider() *schema.Provider { "nsxt_policy_compute_sub_cluster": resourceNsxtPolicyComputeSubCluster(), "nsxt_policy_tier0_inter_vrf_routing": resourceNsxtPolicyTier0InterVRFRouting(), "nsxt_vpc_security_policy": resourceNsxtVPCSecurityPolicy(), + "nsxt_vpc_group": resourceNsxtVPCGroup(), }, ConfigureFunc: providerConfigure, diff --git a/nsxt/resource_nsxt_policy_group.go b/nsxt/resource_nsxt_policy_group.go index e3859a6b6..a405d9eb0 100644 --- a/nsxt/resource_nsxt_policy_group.go +++ b/nsxt/resource_nsxt_policy_group.go @@ -94,42 +94,50 @@ func resourceNsxtPolicyGroup() *schema.Resource { State: nsxtDomainResourceImporter, }, - Schema: map[string]*schema.Schema{ - "nsx_id": getNsxIDSchema(), - "path": getPathSchema(), - "display_name": getDisplayNameSchema(), - "description": getDescriptionSchema(), - "revision": getRevisionSchema(), - "tag": getTagsSchema(), - "context": getContextSchema(false, false, false), - "domain": getDomainNameSchema(), - "group_type": { - Type: schema.TypeString, - Description: "Indicates the group type", - ValidateFunc: validation.StringInSlice(groupTypeValues, false), - Optional: true, - }, - "criteria": { - Type: schema.TypeList, - Description: "Criteria to determine Group membership", - Elem: getCriteriaSetSchema(), - Optional: true, - }, - "conjunction": { - Type: schema.TypeList, - Description: "A conjunction applied to 2 sets of criteria.", - Elem: getConjunctionSchema(), - Optional: true, - }, - "extended_criteria": { - Type: schema.TypeList, - Description: "Extended criteria to determine group membership. extended_criteria is implicitly \"AND\" with criteria", - Elem: getExtendedCriteriaSetSchema(), - Optional: true, - MaxItems: 1, - }, + Schema: getPolicyGroupSchema(true), + } +} + +func getPolicyGroupSchema(withDomain bool) map[string]*schema.Schema { + s := map[string]*schema.Schema{ + "nsx_id": getNsxIDSchema(), + "path": getPathSchema(), + "display_name": getDisplayNameSchema(), + "description": getDescriptionSchema(), + "revision": getRevisionSchema(), + "tag": getTagsSchema(), + "context": getContextSchema(false, false, !withDomain), + "group_type": { + Type: schema.TypeString, + Description: "Indicates the group type", + ValidateFunc: validation.StringInSlice(groupTypeValues, false), + Optional: true, + }, + "criteria": { + Type: schema.TypeList, + Description: "Criteria to determine Group membership", + Elem: getCriteriaSetSchema(), + Optional: true, + }, + "conjunction": { + Type: schema.TypeList, + Description: "A conjunction applied to 2 sets of criteria.", + Elem: getConjunctionSchema(), + Optional: true, + }, + "extended_criteria": { + Type: schema.TypeList, + Description: "Extended criteria to determine group membership. extended_criteria is implicitly \"AND\" with criteria", + Elem: getExtendedCriteriaSetSchema(), + Optional: true, + MaxItems: 1, }, } + + if withDomain { + s["domain"] = getDomainNameSchema() + } + return s } func getIPAddressExpressionSchema() *schema.Resource { @@ -833,10 +841,18 @@ func validateGroupCriteriaAndConjunctions(criteriaSets []interface{}, conjunctio } func resourceNsxtPolicyGroupCreate(d *schema.ResourceData, m interface{}) error { + return resourceNsxtPolicyGroupGeneralCreate(d, m, true) +} + +func resourceNsxtPolicyGroupGeneralCreate(d *schema.ResourceData, m interface{}, withDomain bool) error { connector := getPolicyConnector(m) + domainName := "" + if withDomain { + domainName = d.Get("domain").(string) + } // Initialize resource Id and verify this ID is not yet used - id, err := getOrGenerateID2(d, m, resourceNsxtPolicyGroupExistsInDomainPartial(d.Get("domain").(string))) + id, err := getOrGenerateID2(d, m, resourceNsxtPolicyGroupExistsInDomainPartial(domainName)) if err != nil { return err } @@ -886,7 +902,7 @@ func resourceNsxtPolicyGroupCreate(d *schema.ResourceData, m interface{}) error if client == nil { return policyResourceNotSupportedError() } - err = client.Patch(d.Get("domain").(string), id, obj) + err = client.Patch(domainName, id, obj) // Create the resource using PATCH log.Printf("[INFO] Creating Group with ID %s", id) @@ -897,13 +913,20 @@ func resourceNsxtPolicyGroupCreate(d *schema.ResourceData, m interface{}) error d.SetId(id) d.Set("nsx_id", id) - return resourceNsxtPolicyGroupRead(d, m) + return resourceNsxtPolicyGroupGeneralRead(d, m, withDomain) } func resourceNsxtPolicyGroupRead(d *schema.ResourceData, m interface{}) error { + return resourceNsxtPolicyGroupGeneralRead(d, m, true) +} + +func resourceNsxtPolicyGroupGeneralRead(d *schema.ResourceData, m interface{}, withDomain bool) error { connector := getPolicyConnector(m) id := d.Id() - domainName := d.Get("domain").(string) + domainName := "" + if withDomain { + domainName = d.Get("domain").(string) + } if id == "" { return fmt.Errorf("Error obtaining Group ID") } @@ -920,7 +943,9 @@ func resourceNsxtPolicyGroupRead(d *schema.ResourceData, m interface{}) error { setPolicyTagsInSchema(d, obj.Tags) d.Set("nsx_id", id) d.Set("path", obj.Path) - d.Set("domain", getDomainFromResourcePath(*obj.Path)) + if withDomain { + d.Set("domain", getDomainFromResourcePath(*obj.Path)) + } d.Set("revision", obj.Revision) groupType := "" if len(obj.GroupType) > 0 && util.NsxVersionHigherOrEqual("3.2.0") { @@ -951,6 +976,10 @@ func resourceNsxtPolicyGroupRead(d *schema.ResourceData, m interface{}) error { } func resourceNsxtPolicyGroupUpdate(d *schema.ResourceData, m interface{}) error { + return resourceNsxtPolicyGroupGeneralUpdate(d, m, true) +} + +func resourceNsxtPolicyGroupGeneralUpdate(d *schema.ResourceData, m interface{}, withDomain bool) error { connector := getPolicyConnector(m) id := d.Id() @@ -1007,15 +1036,23 @@ func resourceNsxtPolicyGroupUpdate(d *schema.ResourceData, m interface{}) error } // Update the resource using PATCH - err = client.Patch(d.Get("domain").(string), id, obj) + domainName := "" + if withDomain { + domainName = d.Get("domain").(string) + } + err = client.Patch(domainName, id, obj) if err != nil { return handleUpdateError("Group", id, err) } - return resourceNsxtPolicyGroupRead(d, m) + return resourceNsxtPolicyGroupGeneralRead(d, m, withDomain) } func resourceNsxtPolicyGroupDelete(d *schema.ResourceData, m interface{}) error { + return resourceNsxtPolicyGroupGeneralDelete(d, m, true) +} + +func resourceNsxtPolicyGroupGeneralDelete(d *schema.ResourceData, m interface{}, withDomain bool) error { id := d.Id() if id == "" { return fmt.Errorf("Error obtaining Group ID") @@ -1030,7 +1067,11 @@ func resourceNsxtPolicyGroupDelete(d *schema.ResourceData, m interface{}) error if client == nil { return policyResourceNotSupportedError() } - return client.Delete(d.Get("domain").(string), id, &failIfSubtreeExists, &forceDelete) + domainName := "" + if withDomain { + domainName = d.Get("domain").(string) + } + return client.Delete(domainName, id, &failIfSubtreeExists, &forceDelete) } err := doDelete() diff --git a/nsxt/resource_nsxt_policy_group_test.go b/nsxt/resource_nsxt_policy_group_test.go index f4ab7451e..fe8e47d98 100644 --- a/nsxt/resource_nsxt_policy_group_test.go +++ b/nsxt/resource_nsxt_policy_group_test.go @@ -11,7 +11,8 @@ import ( func TestAccResourceNsxtPolicyGroup_basicImport(t *testing.T) { name := getAccTestResourceName() - testResourceName := "nsxt_policy_group.test" + resourceName := "nsxt_policy_group" + testResourceName := fmt.Sprintf("%s.test", resourceName) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -21,7 +22,7 @@ func TestAccResourceNsxtPolicyGroup_basicImport(t *testing.T) { }, Steps: []resource.TestStep{ { - Config: testAccNsxtPolicyGroupIPAddressImportTemplate(name, false), + Config: testAccNsxtPolicyGroupIPAddressImportTemplate(name, resourceName, false), }, { ResourceName: testResourceName, @@ -34,7 +35,8 @@ func TestAccResourceNsxtPolicyGroup_basicImport(t *testing.T) { func TestAccResourceNsxtPolicyGroup_basicImport_multitenancy(t *testing.T) { name := getAccTestResourceName() - testResourceName := "nsxt_policy_group.test" + resourceName := "nsxt_policy_group" + testResourceName := fmt.Sprintf("%s.test", resourceName) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOnlyMultitenancy(t) }, @@ -44,7 +46,7 @@ func TestAccResourceNsxtPolicyGroup_basicImport_multitenancy(t *testing.T) { }, Steps: []resource.TestStep{ { - Config: testAccNsxtPolicyGroupIPAddressImportTemplate(name, true), + Config: testAccNsxtPolicyGroupIPAddressImportTemplate(name, resourceName, true), }, { ResourceName: testResourceName, @@ -71,7 +73,8 @@ func TestAccResourceNsxtPolicyGroup_addressCriteria_multitenancy(t *testing.T) { func testAccResourceNsxtPolicyGroupAddressCriteria(t *testing.T, withContext bool, preCheck func()) { name := getAccTestResourceName() - testResourceName := "nsxt_policy_group.test" + resourceName := "nsxt_policy_group" + testResourceName := fmt.Sprintf("%s.test", resourceName) resource.ParallelTest(t, resource.TestCase{ PreCheck: preCheck, @@ -81,7 +84,7 @@ func testAccResourceNsxtPolicyGroupAddressCriteria(t *testing.T, withContext boo }, Steps: []resource.TestStep{ { - Config: testAccNsxtPolicyGroupAddressCreateTemplate(name, withContext), + Config: testAccNsxtPolicyGroupAddressCreateTemplate(name, resourceName, withContext), Check: resource.ComposeTestCheckFunc( testAccNsxtPolicyGroupExists(testResourceName, defaultDomain), resource.TestCheckResourceAttr(testResourceName, "display_name", name), @@ -99,7 +102,7 @@ func testAccResourceNsxtPolicyGroupAddressCriteria(t *testing.T, withContext boo ), }, { - Config: testAccNsxtPolicyGroupAddressUpdateTemplate(name, withContext), + Config: testAccNsxtPolicyGroupAddressUpdateTemplate(name, resourceName, withContext), Check: resource.ComposeTestCheckFunc( testAccNsxtPolicyGroupExists(testResourceName, defaultDomain), resource.TestCheckResourceAttr(testResourceName, "display_name", name), @@ -718,27 +721,27 @@ func testAccNsxtPolicyGroupCheckDestroy(state *terraform.State, displayName stri return nil } -func testAccNsxtPolicyGroupIPAddressImportTemplate(name string, withContext bool) string { +func testAccNsxtPolicyGroupIPAddressImportTemplate(name string, resourceName string, withContext bool) string { context := "" if withContext { context = testAccNsxtPolicyMultitenancyContext() } return fmt.Sprintf(` -resource "nsxt_policy_group" "test" { +resource "%s" "test" { %s display_name = "%s" description = "Acceptance Test" } -`, context, name) +`, resourceName, context, name) } -func testAccNsxtPolicyGroupAddressCreateTemplate(name string, withContext bool) string { +func testAccNsxtPolicyGroupAddressCreateTemplate(name, resourceName string, withContext bool) string { context := "" if withContext { context = testAccNsxtPolicyMultitenancyContext() } return fmt.Sprintf(` -resource "nsxt_policy_group" "test" { +resource "%s" "test" { %s display_name = "%s" description = "Acceptance Test" @@ -769,16 +772,16 @@ resource "nsxt_policy_group" "test" { tag = "tag2" } } -`, context, name) +`, resourceName, context, name) } -func testAccNsxtPolicyGroupAddressUpdateTemplate(name string, withContext bool) string { +func testAccNsxtPolicyGroupAddressUpdateTemplate(name, resourceName string, withContext bool) string { context := "" if withContext { context = testAccNsxtPolicyMultitenancyContext() } return fmt.Sprintf(` -resource "nsxt_policy_group" "test" { +resource "%s" "test" { %s display_name = "%s" description = "Acceptance Test" @@ -789,7 +792,7 @@ resource "nsxt_policy_group" "test" { } } } -`, context, name) +`, resourceName, context, name) } func testAccNsxtGlobalPolicyGroupIPAddressCreateTemplate(name string, siteName string) string { @@ -1049,7 +1052,7 @@ resource "nsxt_policy_group" "test" { criteria { ipaddress_expression { - ip_addresses = ["111.1.1.1", "222.2.2.2"] + ip_addresses = ["111.1.1.1-111.1.1.10", "222.2.2.0/24"] } } @@ -1059,7 +1062,7 @@ resource "nsxt_policy_group" "test" { criteria { ipaddress_expression { - ip_addresses = ["111.1.1.4", "222.2.2.4"] + ip_addresses = ["111.1.2.4", "222.2.3.4"] } } diff --git a/nsxt/resource_nsxt_vpc_group.go b/nsxt/resource_nsxt_vpc_group.go new file mode 100644 index 000000000..6bdff0ac1 --- /dev/null +++ b/nsxt/resource_nsxt_vpc_group.go @@ -0,0 +1,36 @@ +/* Copyright © 2024 Broadcom, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + +func resourceNsxtVPCGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceNsxtVPCGroupCreate, + Read: resourceNsxtVPCGroupRead, + Update: resourceNsxtVPCGroupUpdate, + Delete: resourceNsxtVPCGroupDelete, + Importer: &schema.ResourceImporter{ + State: nsxtPolicyPathResourceImporter, + }, + + Schema: getPolicyGroupSchema(false), + } +} + +func resourceNsxtVPCGroupCreate(d *schema.ResourceData, m interface{}) error { + return resourceNsxtPolicyGroupGeneralCreate(d, m, false) +} + +func resourceNsxtVPCGroupRead(d *schema.ResourceData, m interface{}) error { + return resourceNsxtPolicyGroupGeneralRead(d, m, false) +} + +func resourceNsxtVPCGroupUpdate(d *schema.ResourceData, m interface{}) error { + return resourceNsxtPolicyGroupGeneralUpdate(d, m, false) +} + +func resourceNsxtVPCGroupDelete(d *schema.ResourceData, m interface{}) error { + return resourceNsxtPolicyGroupGeneralDelete(d, m, false) +} diff --git a/nsxt/resource_nsxt_vpc_group_test.go b/nsxt/resource_nsxt_vpc_group_test.go new file mode 100644 index 000000000..cbb44e594 --- /dev/null +++ b/nsxt/resource_nsxt_vpc_group_test.go @@ -0,0 +1,173 @@ +/* Copyright © 2024 Broadcom, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccResourceNsxtVPCGroup_basicImport(t *testing.T) { + name := getAccTestResourceName() + resourceName := "nsxt_vpc_group" + testResourceName := fmt.Sprintf("%s.test", resourceName) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccOnlyVPC(t) }, + Providers: testAccProviders, + CheckDestroy: func(state *terraform.State) error { + return testAccNsxtVPCGroupCheckDestroy(state, name) + }, + Steps: []resource.TestStep{ + { + Config: testAccNsxtPolicyGroupIPAddressImportTemplate(name, resourceName, true), + }, + { + ResourceName: testResourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccResourceNsxtPolicyImportIDRetriever(testResourceName), + }, + }, + }) +} + +func TestAccResourceNsxtVPCGroup_addressCriteria(t *testing.T) { + name := getAccTestResourceName() + resourceName := "nsxt_vpc_group" + testResourceName := fmt.Sprintf("%s.test", resourceName) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccOnlyVPC(t) + }, + Providers: testAccProviders, + CheckDestroy: func(state *terraform.State) error { + return testAccNsxtPolicyGroupCheckDestroy(state, name, defaultDomain) + }, + Steps: []resource.TestStep{ + { + Config: testAccNsxtVPCGroupAddressCreateTemplate(name, resourceName, true), + Check: resource.ComposeTestCheckFunc( + testAccNsxtPolicyGroupExists(testResourceName, defaultDomain), + resource.TestCheckResourceAttr(testResourceName, "display_name", name), + resource.TestCheckResourceAttr(testResourceName, "description", "Acceptance Test"), + resource.TestCheckResourceAttrSet(testResourceName, "path"), + resource.TestCheckResourceAttrSet(testResourceName, "revision"), + resource.TestCheckResourceAttr(testResourceName, "context.#", "1"), + resource.TestCheckResourceAttr(testResourceName, "context.0.project_id", os.Getenv("NSXT_VPC_PROJECT_ID")), + resource.TestCheckResourceAttr(testResourceName, "context.0.vpc_id", os.Getenv("NSXT_VPC_ID")), + resource.TestCheckResourceAttr(testResourceName, "conjunction.#", "1"), + resource.TestCheckResourceAttr(testResourceName, "tag.#", "2"), + resource.TestCheckResourceAttr(testResourceName, "criteria.#", "2"), + ), + }, + { + Config: testAccNsxtVPCGroupAddressUpdateTemplate(name, resourceName, true), + Check: resource.ComposeTestCheckFunc( + testAccNsxtPolicyGroupExists(testResourceName, defaultDomain), + resource.TestCheckResourceAttr(testResourceName, "display_name", name), + resource.TestCheckResourceAttr(testResourceName, "description", "Acceptance Test"), + resource.TestCheckResourceAttrSet(testResourceName, "path"), + resource.TestCheckResourceAttrSet(testResourceName, "revision"), + resource.TestCheckResourceAttr(testResourceName, "context.#", "1"), + resource.TestCheckResourceAttr(testResourceName, "context.0.project_id", os.Getenv("NSXT_VPC_PROJECT_ID")), + resource.TestCheckResourceAttr(testResourceName, "context.0.vpc_id", os.Getenv("NSXT_VPC_ID")), + resource.TestCheckResourceAttr(testResourceName, "conjunction.#", "0"), + resource.TestCheckResourceAttr(testResourceName, "tag.#", "0"), + resource.TestCheckResourceAttr(testResourceName, "criteria.#", "1"), + resource.TestCheckResourceAttr(testResourceName, "criteria.0.ipaddress_expression.#", "1"), + resource.TestCheckResourceAttr(testResourceName, "criteria.0.ipaddress_expression.0.ip_addresses.#", "1"), + resource.TestCheckResourceAttr(testResourceName, "criteria.1.macaddress_expression.#", "0"), + ), + }, + }, + }) +} + +func testAccNsxtVPCGroupCheckDestroy(state *terraform.State, displayName string) error { + connector := getPolicyConnector(testAccProvider.Meta().(nsxtClients)) + + for _, rs := range state.RootModule().Resources { + + if rs.Type != "nsxt_vpc_group" { + continue + } + + resourceID := rs.Primary.Attributes["id"] + exists, err := resourceNsxtPolicyGroupExistsInDomain(testAccGetSessionContext(), resourceID, "", connector) + if err != nil { + return err + } + if exists { + return fmt.Errorf("policy Group %s still exists", displayName) + } + } + return nil +} + +func testAccNsxtVPCGroupAddressCreateTemplate(name, resourceName string, withContext bool) string { + context := "" + if withContext { + context = testAccNsxtPolicyMultitenancyContext() + } + return fmt.Sprintf(` +resource "%s" "test" { +%s + display_name = "%s" + description = "Acceptance Test" + + criteria { + ipaddress_expression { + ip_addresses = ["111.1.1.1", "222.2.2.2"] + } + } + + conjunction { + operator = "OR" + } + + criteria { + ipaddress_expression { + ip_addresses = ["122.1.1.1"] + } + } + + tag { + scope = "scope1" + tag = "tag1" + } + + tag { + scope = "scope2" + tag = "tag2" + } +} +`, resourceName, context, name) +} + +func testAccNsxtVPCGroupAddressUpdateTemplate(name, resourceName string, withContext bool) string { + context := "" + if withContext { + context = testAccNsxtPolicyMultitenancyContext() + } + return fmt.Sprintf(` +resource "%s" "test" { +%s + display_name = "%s" + description = "Acceptance Test" + + criteria { + ipaddress_expression { + ip_addresses = ["122.1.1.1"] + } + } +} +`, resourceName, context, name) +} diff --git a/tools/api-wrapper-generator.py b/tools/api-wrapper-generator.py index eae9fa9a7..6f64837b6 100644 --- a/tools/api-wrapper-generator.py +++ b/tools/api-wrapper-generator.py @@ -71,6 +71,12 @@ def new_func_call_setup(api, subs_dict): return '%s(%s)' % (g[0], ', '.join(arg_list)) +def find_api_package_attributes(api, type): + for a in api['api_packages']: + if type == a['type']: + return a + + def api_func_call_setup(api, subs_dict): g = parse_api_call(subs_dict['func_def']) arg_list = get_arglist(g[2]) @@ -79,6 +85,11 @@ def api_func_call_setup(api, subs_dict): elif subs_dict['type'] == "VPC": arg_list = ['utl.DefaultOrgID', 'c.ProjectID', 'c.VPCID'] + arg_list arg_list.remove('domainIdParam') + attrs = find_api_package_attributes(api, subs_dict['type']) + x = attrs.get('ignore_params', {}) + for n in attrs.get('ignore_params', {}).get(g[1], []): + arg_list.remove(n) + return '%s(%s)' % (g[1], ', '.join(arg_list)) diff --git a/website/docs/r/vpc_group.html.markdown b/website/docs/r/vpc_group.html.markdown new file mode 100644 index 000000000..cada321bb --- /dev/null +++ b/website/docs/r/vpc_group.html.markdown @@ -0,0 +1,107 @@ +--- +subcategory: "VPC" +layout: "nsxt" +page_title: "NSXT: nsxt_vpc_group" +description: A resource to configure a VPC Group and its members. +--- + +# nsxt_vpc_group + +This resource provides a method for the management of an inventory Group and its members in a VPC scope. Groups as often +used as sources and destinations, as well as in the Applied To field, in firewall rules. + +This resource is applicable to NSX Policy Manager. + +```hcl +data "nsxt_policy_project" "demoproj" { + display_name = "demoproj" +} + +data "nsxt_policy_vpc" "demovpc" { + context { + project_id = data.nsxt_policy_project.demoproj.id + } + display_name = "vpc1" +} + +resource "nsxt_vpc_group" "group1" { + context { + project_id = data.nsxt_policy_project.demoproj.id + vpc_id = data.nsxt_policy_vpc.demovpc.id + } + + display_name = "tf-group1" + description = "Terraform provisioned Group" + + criteria { + condition { + key = "Name" + member_type = "VirtualMachine" + operator = "STARTSWITH" + value = "public" + } + condition { + key = "OSName" + member_type = "VirtualMachine" + operator = "CONTAINS" + value = "Ubuntu" + } + } +} +``` + +## Argument Reference + +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 Group. +* `nsx_id` - (Optional) The NSX ID of this resource. If set, this ID will be used to create the group resource. +* `context` - (Required) The context which the object belongs to + * `project_id` - (Required) The ID of the project which the object belongs to + * `vpc_id` - (Required) The ID of the VPC which the object belongs to +* `criteria` - (Optional) A repeatable block to specify criteria for members of this Group. If more than 1 criteria block is specified, it must be separated by a `conjunction`. In a `criteria` block the following membership selection expressions can be used: + * `ipaddress_expression` - (Optional) An expression block to specify individual IP Addresses, ranges of IP Addresses or subnets for this Group. + * `ip_addresses` - (Required) This list can consist of a single IP address, IP address range or a subnet. Its type can be of either IPv4 or IPv6. Both IPv4 and IPv6 addresses within one expression is not allowed. + * `macaddress_expression` - (Optional) An expression block to specify individual MAC Addresses for this Group. + * `mac_addresses` - (Required) List of MAC addresses. + * `path_expression` - (Optional) An expression block to specify direct group members by policy path. + * `member_paths` - (Required) List of policy paths for direct members for this Group (such as Segments, Segment ports, Groups etc). + * `external_id_expression` - (Optional) An expression block to specify external IDs for the specified member type for this Group. + * `member_type` - (Optional) External ID member type. Must be one of: `VirtualMachine`, `VirtualNetworkInterface`, `CloudNativeServiceInstance`, or `PhysicalServer`. Defaults to `VirtualMachine`. + * `external_ids` - (Required) List of external IDs for the specified member type. + * `condition` (Optional) A repeatable condition block to select this Group's members. When multiple `condition` blocks are used in a single `criteria` they form a nested expression that's implicitly ANDed together and each nested condition must used the same `member_type`. + * `key` (Required) Specifies the attribute to query. Must be one of: `Tag`, `ComputerName`, `OSName`, `Name`, `NodeType`, `GroupType`, `ALL`, `IPAddress`, `PodCidr`. Please note that certain keys are only applicable to certain member types. + * `member_type` (Required) Specifies the type of resource to query. Must be one of: `IPSet`, `LogicalPort`, `LogicalSwitch`, `Segment`, `SegmentPort`, `VirtualMachine`, `Group`, `DVPG`, `DVPort`, `IPAddress`, `TransportNode`, `Pod`. `Service`, `Namespace`, `KubernetesCluster`, `KubernetesNamespace`, `KubernetesIngress`, `KubernetesService`, `KubernetesNode`, `AntreaEgress`, `AntreaIPPool`. Not that certain member types are only applicable to certain environments. + * `operator` (Required) Specifies the query operator to use. Must be one of: `CONTAINS`, `ENDSWITH`, `EQUALS`, `NOTEQUALS`, `STARTSWITH`, `IN`, `NOTIN`, `MATCHES`. Not that certain operators are only applicable to certain keys/member types.:w + * `value` (Required) User specified string value to use in the query. For `Tag` criteria, use 'scope|value' notation if you wish to specify scope in criteria. +* `conjunction` (Required for multiple `criteria`) When specifying multiple `criteria`, a conjunction is used to specify if the criteria should selected using `AND` or `OR`. + * `operator` (Required) The operator to use. Must be one of `AND` or `OR`. If `AND` is used, then the `criteria` block before/after must be of the same type and if using `condition` then also must use the same `member_type`. +* `extended_criteria` (Optional) A condition block to specify higher level context to include in this Group's members. (e.g. user AD group). This configuration is for Local Manager only. Currently only one block is supported by NSX. Note that `extended_criteria` is implicitly `AND` with `criteria`. + * `identity_group` (Optional) A repeatable condition block selecting user AD groups to be included in this Group. Note that `identity_groups` are `OR` with each other. + * `distinguished_name` (Required) LDAP distinguished name (DN). A valid fully qualified distinguished name should be provided here. This value is valid only if it matches to exactly 1 LDAP object on the LDAP server. + * `domain_base_distinguished_name` (Required) Identity (Directory) domain base distinguished name. This is the base distinguished name for the domain where this identity group resides. (e.g. dc=example,dc=com) + * `sid` (Optional) Identity (Directory) Group SID (security identifier). A security identifier (SID) is a unique value of variable length used to identify a trustee. This field is only populated for Microsoft Active Directory identity store. +* `group_type` - (Optional) One of `IPAddress`, `ANTREA`. Empty group type indicates a generic group. Attribute is supported with NSX version 3.2.0 and above. + + +## Attributes Reference + +In addition to arguments listed above, the following attributes are exported: + +* `id` - ID of the Group. +* `revision` - Indicates current revision number of the object as seen by NSX-T API server. This attribute can be useful for debugging. +* `path` - The NSX path of the policy resource. + +## Importing + +An existing VPC Group can be [imported][docs-import] into this resource, via the following command: + +[docs-import]: https://www.terraform.io/cli/import + +``` +terraform import nsxt_vpc_group.group1 PATH +``` + +The above command imports the VPC Group named `group` with the NSX Policy path `PATH`.