diff --git a/api/api_list.yaml b/api/api_list.yaml index e56339ed2..527539beb 100644 --- a/api/api_list.yaml +++ b/api/api_list.yaml @@ -191,6 +191,9 @@ - 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 model_name: GatewayPolicy obj_name: GatewayPolicy client_name: GatewayPoliciesClient diff --git a/api/infra/domains/gateway_policy.go b/api/infra/domains/gateway_policy.go index 5bced1e1a..d52139852 100644 --- a/api/infra/domains/gateway_policy.go +++ b/api/infra/domains/gateway_policy.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 NewGatewayPoliciesClient(sessionContext utl.SessionContext, connector vapiP case utl.Multitenancy: client = client2.NewGatewayPoliciesClient(connector) + case utl.VPC: + client = client3.NewGatewayPoliciesClient(connector) + default: return nil } @@ -67,6 +71,13 @@ func (c GatewayPolicyClientContext) Get(domainIdParam string, gatewayPolicyIdPar return obj, err } + case utl.VPC: + client := c.Client.(client3.GatewayPoliciesClient) + obj, err = client.Get(utl.DefaultOrgID, c.ProjectID, c.VPCID, gatewayPolicyIdParam) + if err != nil { + return obj, err + } + default: return obj, errors.New("invalid infrastructure for model") } @@ -94,6 +105,10 @@ func (c GatewayPolicyClientContext) Patch(domainIdParam string, gatewayPolicyIdP client := c.Client.(client2.GatewayPoliciesClient) err = client.Patch(utl.DefaultOrgID, c.ProjectID, domainIdParam, gatewayPolicyIdParam, gatewayPolicyParam) + case utl.VPC: + client := c.Client.(client3.GatewayPoliciesClient) + err = client.Patch(utl.DefaultOrgID, c.ProjectID, c.VPCID, gatewayPolicyIdParam, gatewayPolicyParam) + default: err = errors.New("invalid infrastructure for model") } @@ -130,6 +145,10 @@ func (c GatewayPolicyClientContext) Update(domainIdParam string, gatewayPolicyId client := c.Client.(client2.GatewayPoliciesClient) obj, err = client.Update(utl.DefaultOrgID, c.ProjectID, domainIdParam, gatewayPolicyIdParam, gatewayPolicyParam) + case utl.VPC: + client := c.Client.(client3.GatewayPoliciesClient) + obj, err = client.Update(utl.DefaultOrgID, c.ProjectID, c.VPCID, gatewayPolicyIdParam, gatewayPolicyParam) + default: err = errors.New("invalid infrastructure for model") } @@ -153,6 +172,10 @@ func (c GatewayPolicyClientContext) Delete(domainIdParam string, gatewayPolicyId client := c.Client.(client2.GatewayPoliciesClient) err = client.Delete(utl.DefaultOrgID, c.ProjectID, domainIdParam, gatewayPolicyIdParam) + case utl.VPC: + client := c.Client.(client3.GatewayPoliciesClient) + err = client.Delete(utl.DefaultOrgID, c.ProjectID, c.VPCID, gatewayPolicyIdParam) + default: err = errors.New("invalid infrastructure for model") } @@ -185,6 +208,10 @@ func (c GatewayPolicyClientContext) List(domainIdParam string, cursorParam *stri client := c.Client.(client2.GatewayPoliciesClient) obj, err = client.List(utl.DefaultOrgID, c.ProjectID, domainIdParam, cursorParam, includeMarkForDeleteObjectsParam, includeRuleCountParam, includedFieldsParam, pageSizeParam, sortAscendingParam, sortByParam) + case utl.VPC: + client := c.Client.(client3.GatewayPoliciesClient) + obj, err = client.List(utl.DefaultOrgID, c.ProjectID, c.VPCID, cursorParam, includeMarkForDeleteObjectsParam, includeRuleCountParam, includedFieldsParam, pageSizeParam, sortAscendingParam, sortByParam) + default: err = errors.New("invalid infrastructure for model") } diff --git a/nsxt/policy_common.go b/nsxt/policy_common.go index 24225f83c..a5ffe53d3 100644 --- a/nsxt/policy_common.go +++ b/nsxt/policy_common.go @@ -310,13 +310,13 @@ func getSecurityPolicyAndGatewayRuleSchema(scopeRequired bool, isIds bool, nsxID return ruleSchema } -func getPolicyGatewayPolicySchema() map[string]*schema.Schema { - secPolicy := getPolicySecurityPolicySchema(false, true, true, true) +func getPolicyGatewayPolicySchema(withDomain bool) map[string]*schema.Schema { + secPolicy := getPolicySecurityPolicySchema(false, true, true, withDomain) // GW Policies don't support scope delete(secPolicy, "scope") secPolicy["category"].ValidateFunc = validation.StringInSlice(gatewayPolicyCategoryWritableValues, false) // GW Policy rules require scope to be set - secPolicy["rule"] = getSecurityPolicyAndGatewayRulesSchema(true, false, true) + secPolicy["rule"] = getSecurityPolicyAndGatewayRulesSchema(withDomain, false, true) return secPolicy } diff --git a/nsxt/provider.go b/nsxt/provider.go index 219c61f93..d50f7eec1 100644 --- a/nsxt/provider.go +++ b/nsxt/provider.go @@ -498,6 +498,7 @@ func Provider() *schema.Provider { "nsxt_policy_tier0_inter_vrf_routing": resourceNsxtPolicyTier0InterVRFRouting(), "nsxt_vpc_security_policy": resourceNsxtVPCSecurityPolicy(), "nsxt_vpc_group": resourceNsxtVPCGroup(), + "nsxt_vpc_gateway_policy": resourceNsxtVPCGatewayPolicy(), }, ConfigureFunc: providerConfigure, diff --git a/nsxt/resource_nsxt_policy_gateway_policy.go b/nsxt/resource_nsxt_policy_gateway_policy.go index 2e72e6be8..ff4b62c8c 100644 --- a/nsxt/resource_nsxt_policy_gateway_policy.go +++ b/nsxt/resource_nsxt_policy_gateway_policy.go @@ -26,11 +26,11 @@ func resourceNsxtPolicyGatewayPolicy() *schema.Resource { State: nsxtDomainResourceImporter, }, - Schema: getPolicyGatewayPolicySchema(), + Schema: getPolicyGatewayPolicySchema(true), } } -func getGatewayPolicyInDomain(sessionContext utl.SessionContext, id string, domainName string, connector client.Connector) (model.GatewayPolicy, error) { +func getGatewayPolicy(sessionContext utl.SessionContext, id string, domainName string, connector client.Connector) (model.GatewayPolicy, error) { client := domains.NewGatewayPoliciesClient(sessionContext, connector) if client == nil { return model.GatewayPolicy{}, policyResourceNotSupportedError() @@ -40,7 +40,7 @@ func getGatewayPolicyInDomain(sessionContext utl.SessionContext, id string, doma } func resourceNsxtPolicyGatewayPolicyExistsInDomain(sessionContext utl.SessionContext, id string, domainName string, connector client.Connector) (bool, error) { - _, err := getGatewayPolicyInDomain(sessionContext, id, domainName, connector) + _, err := getGatewayPolicy(sessionContext, id, domainName, connector) if err == nil { return true, nil @@ -124,9 +124,12 @@ func getUpdatedRuleChildren(d *schema.ResourceData) ([]*data.StructValue, error) } -func policyGatewayPolicyBuildAndPatch(d *schema.ResourceData, m interface{}, connector client.Connector, isGlobalManager bool, id string) error { +func policyGatewayPolicyBuildAndPatch(d *schema.ResourceData, m interface{}, connector client.Connector, isGlobalManager bool, id string, withDomain bool) error { - domain := d.Get("domain").(string) + domain := "" + if withDomain { + domain = d.Get("domain").(string) + } displayName := d.Get("display_name").(string) description := d.Get("description").(string) tags := getPolicyTagsFromSchema(d) @@ -181,7 +184,7 @@ func resourceNsxtPolicyGatewayPolicyCreate(d *schema.ResourceData, m interface{} return err } - err = policyGatewayPolicyBuildAndPatch(d, m, connector, isPolicyGlobalManager(m), id) + err = policyGatewayPolicyBuildAndPatch(d, m, connector, isPolicyGlobalManager(m), id, true) if err != nil { return handleCreateError("Gateway Policy", id, err) } @@ -200,7 +203,7 @@ func resourceNsxtPolicyGatewayPolicyRead(d *schema.ResourceData, m interface{}) return fmt.Errorf("Error obtaining Gateway Policy ID") } - obj, err := getGatewayPolicyInDomain(getSessionContext(d, m), id, d.Get("domain").(string), connector) + obj, err := getGatewayPolicy(getSessionContext(d, m), id, d.Get("domain").(string), connector) if err != nil { return handleReadError(d, "Gateway Policy", id, err) } @@ -232,7 +235,7 @@ func resourceNsxtPolicyGatewayPolicyUpdate(d *schema.ResourceData, m interface{} return fmt.Errorf("Error obtaining Gateway Policy ID") } - err := policyGatewayPolicyBuildAndPatch(d, m, connector, isPolicyGlobalManager(m), id) + err := policyGatewayPolicyBuildAndPatch(d, m, connector, isPolicyGlobalManager(m), id, true) if err != nil { return handleUpdateError("Gateway Policy", id, err) } diff --git a/nsxt/resource_nsxt_policy_gateway_policy_test.go b/nsxt/resource_nsxt_policy_gateway_policy_test.go index 122b1832a..a081444b4 100644 --- a/nsxt/resource_nsxt_policy_gateway_policy_test.go +++ b/nsxt/resource_nsxt_policy_gateway_policy_test.go @@ -27,7 +27,8 @@ func TestAccResourceNsxtPolicyGatewayPolicy_multitenancy(t *testing.T) { func testAccResourceNsxtPolicyGatewayPolicyBasic(t *testing.T, withContext bool, preCheck func()) { name := getAccTestResourceName() updatedName := getAccTestResourceName() - testResourceName := "nsxt_policy_gateway_policy.test" + resourceName := "nsxt_policy_gateway_policy" + testResourceName := fmt.Sprintf("%s.test", resourceName) comments1 := "Acceptance test create" comments2 := "Acceptance test update" direction1 := "IN" @@ -46,7 +47,7 @@ func testAccResourceNsxtPolicyGatewayPolicyBasic(t *testing.T, withContext bool, }, Steps: []resource.TestStep{ { - Config: testAccNsxtPolicyGatewayPolicyBasic(name, comments1, withContext), + Config: testAccNsxtPolicyGatewayPolicyBasic(resourceName, name, comments1, withContext), Check: resource.ComposeTestCheckFunc( testAccNsxtPolicyGatewayPolicyExists(testResourceName, defaultDomain), resource.TestCheckResourceAttr(testResourceName, "display_name", name), @@ -63,7 +64,7 @@ func testAccResourceNsxtPolicyGatewayPolicyBasic(t *testing.T, withContext bool, ), }, { - Config: testAccNsxtPolicyGatewayPolicyBasic(updatedName, comments2, withContext), + Config: testAccNsxtPolicyGatewayPolicyBasic(resourceName, updatedName, comments2, withContext), Check: resource.ComposeTestCheckFunc( testAccNsxtPolicyGatewayPolicyExists(testResourceName, defaultDomain), resource.TestCheckResourceAttr(testResourceName, "display_name", updatedName), @@ -79,7 +80,7 @@ func testAccResourceNsxtPolicyGatewayPolicyBasic(t *testing.T, withContext bool, ), }, { - Config: testAccNsxtPolicyGatewayPolicyWithRule(updatedName, direction1, proto1, tag1, withContext), + Config: testAccNsxtPolicyGatewayPolicyWithRule(resourceName, updatedName, direction1, proto1, tag1, withContext), Check: resource.ComposeTestCheckFunc( testAccNsxtPolicyGatewayPolicyExists(testResourceName, defaultDomain), resource.TestCheckResourceAttr(testResourceName, "display_name", updatedName), @@ -102,7 +103,7 @@ func testAccResourceNsxtPolicyGatewayPolicyBasic(t *testing.T, withContext bool, ), }, { - Config: testAccNsxtPolicyGatewayPolicyWithRule(updatedName, direction2, proto2, tag2, withContext), + Config: testAccNsxtPolicyGatewayPolicyWithRule(resourceName, updatedName, direction2, proto2, tag2, withContext), Check: resource.ComposeTestCheckFunc( testAccNsxtPolicyGatewayPolicyExists(testResourceName, defaultDomain), resource.TestCheckResourceAttr(testResourceName, "display_name", updatedName), @@ -208,7 +209,8 @@ func TestAccResourceNsxtPolicyGatewayPolicy_withDependencies(t *testing.T) { } func TestAccResourceNsxtPolicyGatewayPolicy_importBasic(t *testing.T) { name := getAccTestResourceName() - testResourceName := "nsxt_policy_gateway_policy.test" + resourceName := "nsxt_policy_gateway_policy" + testResourceName := fmt.Sprintf("%s.test", resourceName) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -218,7 +220,7 @@ func TestAccResourceNsxtPolicyGatewayPolicy_importBasic(t *testing.T) { }, Steps: []resource.TestStep{ { - Config: testAccNsxtPolicyGatewayPolicyBasic(name, "import", false), + Config: testAccNsxtPolicyGatewayPolicyBasic(resourceName, name, "import", false), }, { ResourceName: testResourceName, @@ -231,7 +233,8 @@ func TestAccResourceNsxtPolicyGatewayPolicy_importBasic(t *testing.T) { func TestAccResourceNsxtPolicyGatewayPolicy_importBasic_multitenancy(t *testing.T) { name := getAccTestResourceName() - testResourceName := "nsxt_policy_gateway_policy.test" + resourceName := "nsxt_policy_gateway_policy" + testResourceName := fmt.Sprintf("%s.test", resourceName) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOnlyMultitenancy(t) }, @@ -241,7 +244,7 @@ func TestAccResourceNsxtPolicyGatewayPolicy_importBasic_multitenancy(t *testing. }, Steps: []resource.TestStep{ { - Config: testAccNsxtPolicyGatewayPolicyBasic(name, "import", true), + Config: testAccNsxtPolicyGatewayPolicyBasic(resourceName, name, "import", true), }, { ResourceName: testResourceName, @@ -623,13 +626,13 @@ resource "nsxt_policy_gateway_policy" "test" { }`, name, comments) } -func testAccNsxtPolicyGatewayPolicyBasic(name string, comments string, withContext bool) string { +func testAccNsxtPolicyGatewayPolicyBasic(resourceName, name, comments string, withContext bool) string { context := "" if withContext { context = testAccNsxtPolicyMultitenancyContext() } return fmt.Sprintf(` -resource "nsxt_policy_gateway_policy" "test" { +resource "%s" "test" { %s display_name = "%s" description = "Acceptance Test" @@ -645,10 +648,10 @@ resource "nsxt_policy_gateway_policy" "test" { tag = "orange" } -}`, context, name, comments) +}`, resourceName, context, name, comments) } -func testAccNsxtPolicyGatewayPolicyWithRule(name string, direction string, protocol string, ruleTag string, withContext bool) string { +func testAccNsxtPolicyGatewayPolicyWithRule(resourceName, name, direction, protocol, ruleTag string, withContext bool) string { context := "" if withContext { context = testAccNsxtPolicyMultitenancyContext() @@ -660,7 +663,7 @@ resource "nsxt_policy_tier1_gateway" "gwt1test" { description = "Acceptance Test" } -resource "nsxt_policy_gateway_policy" "test" { +resource "%s" "test" { %s display_name = "%s" description = "Acceptance Test" @@ -687,7 +690,7 @@ resource "nsxt_policy_gateway_policy" "test" { tag = "blue" } } -}`, context, context, name, name, direction, protocol, ruleTag) +}`, context, resourceName, context, name, name, direction, protocol, ruleTag) } // TODO: add profiles when available diff --git a/nsxt/resource_nsxt_policy_predefined_gateway_policy.go b/nsxt/resource_nsxt_policy_predefined_gateway_policy.go index 5a7f002a0..377e12df6 100644 --- a/nsxt/resource_nsxt_policy_predefined_gateway_policy.go +++ b/nsxt/resource_nsxt_policy_predefined_gateway_policy.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + nsxt "github.com/vmware/vsphere-automation-sdk-go/services/nsxt" "github.com/vmware/vsphere-automation-sdk-go/runtime/bindings" "github.com/vmware/vsphere-automation-sdk-go/runtime/data" @@ -264,6 +265,21 @@ func createChildDomainWithGatewayPolicy(domain string, policyID string, policy m } func gatewayPolicyInfraPatch(context utl.SessionContext, policy model.GatewayPolicy, domain string, m interface{}) error { + connector := getPolicyConnector(m) + if context.ClientType == utl.VPC { + childVPC, err := createChildVPCWithGatewayPolicy(context, *policy.Id, policy) + if err != nil { + return fmt.Errorf("failed to create H-API for VPC Gateway Policy: %s", err) + } + + orgRoot := model.OrgRoot{ + ResourceType: strPtr("OrgRoot"), + Children: []*data.StructValue{childVPC}, + } + + client := nsxt.NewOrgRootClient(connector) + return client.Patch(orgRoot, nil) + } childDomain, err := createChildDomainWithGatewayPolicy(domain, *policy.Id, policy) if err != nil { return fmt.Errorf("Failed to create H-API for Predefined Gateway Policy: %s", err) @@ -293,7 +309,7 @@ func updatePolicyPredefinedGatewayPolicy(id string, d *schema.ResourceData, m in return fmt.Errorf("Failed to extract domain from Gateway Policy path %s", path) } - predefinedPolicy, err := getGatewayPolicyInDomain(getSessionContext(d, m), id, domain, connector) + predefinedPolicy, err := getGatewayPolicy(getSessionContext(d, m), id, domain, connector) if err != nil { return err } @@ -468,7 +484,7 @@ func resourceNsxtPolicyPredefinedGatewayPolicyDelete(d *schema.ResourceData, m i path := d.Get("path").(string) domain := getDomainFromResourcePath(path) - predefinedPolicy, err := getGatewayPolicyInDomain(getSessionContext(d, m), id, domain, getPolicyConnector(m)) + predefinedPolicy, err := getGatewayPolicy(getSessionContext(d, m), id, domain, getPolicyConnector(m)) if err != nil { return err } diff --git a/nsxt/resource_nsxt_vpc_gateway_policy.go b/nsxt/resource_nsxt_vpc_gateway_policy.go new file mode 100644 index 000000000..f39cb9b26 --- /dev/null +++ b/nsxt/resource_nsxt_vpc_gateway_policy.go @@ -0,0 +1,163 @@ +/* Copyright © 2024 Broadcom, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/vmware/vsphere-automation-sdk-go/runtime/bindings" + "github.com/vmware/vsphere-automation-sdk-go/runtime/data" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model" + + "github.com/vmware/terraform-provider-nsxt/api/infra/domains" + utl "github.com/vmware/terraform-provider-nsxt/api/utl" +) + +func resourceNsxtVPCGatewayPolicy() *schema.Resource { + return &schema.Resource{ + Create: resourceNsxtVPCGatewayPolicyCreate, + Read: resourceNsxtVPCGatewayPolicyRead, + Update: resourceNsxtVPCGatewayPolicyUpdate, + Delete: resourceNsxtVPCGatewayPolicyDelete, + Importer: &schema.ResourceImporter{ + State: nsxtPolicyPathResourceImporter, + }, + + Schema: getPolicyGatewayPolicySchema(false), + } +} + +func resourceNsxtVPCGatewayPolicyCreate(d *schema.ResourceData, m interface{}) error { + connector := getPolicyConnector(m) + + // Initialize resource Id and verify this ID is not yet used + id, err := getOrGenerateID2(d, m, resourceNsxtPolicyGatewayPolicyExistsPartial("")) + if err != nil { + return err + } + + err = policyGatewayPolicyBuildAndPatch(d, m, connector, isPolicyGlobalManager(m), id, false) + if err != nil { + return handleCreateError("VPC Gateway Policy", id, err) + } + + d.SetId(id) + d.Set("nsx_id", id) + + return resourceNsxtVPCGatewayPolicyRead(d, m) +} + +func resourceNsxtVPCGatewayPolicyRead(d *schema.ResourceData, m interface{}) error { + connector := getPolicyConnector(m) + + id := d.Id() + if id == "" { + return fmt.Errorf("error obtaining VPC Gateway Policy ID") + } + + obj, err := getGatewayPolicy(getSessionContext(d, m), id, "", connector) + if err != nil { + return handleReadError(d, "VPC Gateway Policy", id, err) + } + + d.Set("display_name", obj.DisplayName) + d.Set("description", obj.Description) + setPolicyTagsInSchema(d, obj.Tags) + d.Set("nsx_id", id) + d.Set("path", obj.Path) + d.Set("category", obj.Category) + d.Set("comments", obj.Comments) + d.Set("locked", obj.Locked) + d.Set("sequence_number", obj.SequenceNumber) + d.Set("stateful", obj.Stateful) + if obj.TcpStrict != nil { + // tcp_strict is dependent on stateful and maybe nil + d.Set("tcp_strict", *obj.TcpStrict) + } + d.Set("revision", obj.Revision) + return setPolicyRulesInSchema(d, obj.Rules) +} + +func resourceNsxtVPCGatewayPolicyUpdate(d *schema.ResourceData, m interface{}) error { + connector := getPolicyConnector(m) + + id := d.Id() + if id == "" { + return fmt.Errorf("error obtaining VPC Gateway Policy ID") + } + + err := policyGatewayPolicyBuildAndPatch(d, m, connector, isPolicyGlobalManager(m), id, false) + if err != nil { + return handleUpdateError("VPC Gateway Policy", id, err) + } + + return resourceNsxtVPCGatewayPolicyRead(d, m) +} + +func resourceNsxtVPCGatewayPolicyDelete(d *schema.ResourceData, m interface{}) error { + id := d.Id() + if id == "" { + return fmt.Errorf("error obtaining VPC Gateway Policy ID") + } + + connector := getPolicyConnector(m) + client := domains.NewGatewayPoliciesClient(getSessionContext(d, m), connector) + err := client.Delete("", id) + if err != nil { + return handleDeleteError("VPC Gateway Policy", id, err) + } + + return nil +} + +func createChildVPCWithGatewayPolicy(context utl.SessionContext, policyID string, policy model.GatewayPolicy) (*data.StructValue, error) { + converter := bindings.NewTypeConverter() + + childPolicy := model.ChildGatewayPolicy{ + Id: &policyID, + ResourceType: "ChildGatewayPolicy", + GatewayPolicy: &policy, + } + + dataValue, errors := converter.ConvertToVapi(childPolicy, model.ChildGatewayPolicyBindingType()) + if len(errors) > 0 { + return nil, errors[0] + } + + childVPC := model.ChildResourceReference{ + Id: &context.VPCID, + ResourceType: "ChildResourceReference", + TargetType: strPtr("Vpc"), + Children: []*data.StructValue{dataValue.(*data.StructValue)}, + } + + dataValue, errors = converter.ConvertToVapi(childVPC, model.ChildResourceReferenceBindingType()) + if len(errors) > 0 { + return nil, errors[0] + } + childProject := model.ChildResourceReference{ + Id: &context.ProjectID, + ResourceType: "ChildResourceReference", + TargetType: strPtr("Project"), + Children: []*data.StructValue{dataValue.(*data.StructValue)}, + } + dataValue, errors = converter.ConvertToVapi(childProject, model.ChildResourceReferenceBindingType()) + if len(errors) > 0 { + return nil, errors[0] + } + + childOrg := model.ChildResourceReference{ + Id: strPtr(defaultOrgID), + ResourceType: "ChildResourceReference", + TargetType: strPtr("Org"), + Children: []*data.StructValue{dataValue.(*data.StructValue)}, + } + dataValue, errors = converter.ConvertToVapi(childOrg, model.ChildResourceReferenceBindingType()) + if len(errors) > 0 { + return nil, errors[0] + } + + return dataValue.(*data.StructValue), nil +} diff --git a/nsxt/resource_nsxt_vpc_gateway_policy_test.go b/nsxt/resource_nsxt_vpc_gateway_policy_test.go new file mode 100644 index 000000000..8f2e7b9a2 --- /dev/null +++ b/nsxt/resource_nsxt_vpc_gateway_policy_test.go @@ -0,0 +1,178 @@ +/* Copyright © 2024 Broadcom, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccResourceNsxtVPCGatewayPolicy_basic(t *testing.T) { + name := getAccTestResourceName() + updatedName := getAccTestResourceName() + resourceName := "nsxt_vpc_gateway_policy" + testResourceName := fmt.Sprintf("%s.test", resourceName) + comments1 := "Acceptance test create" + comments2 := "Acceptance test update" + direction1 := "IN" + direction2 := "OUT" + proto1 := "IPV4" + proto2 := "IPV4_IPV6" + defaultAction := "ALLOW" + tag1 := "abc" + tag2 := "def" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccOnlyVPC(t) + }, + Providers: testAccProviders, + CheckDestroy: func(state *terraform.State) error { + return testAccNsxtPolicyGatewayPolicyCheckDestroy(state, updatedName, defaultDomain) + }, + Steps: []resource.TestStep{ + { + Config: testAccNsxtPolicyGatewayPolicyBasic(resourceName, name, comments1, true), + Check: resource.ComposeTestCheckFunc( + testAccNsxtPolicyGatewayPolicyExists(testResourceName, defaultDomain), + resource.TestCheckResourceAttr(testResourceName, "display_name", name), + resource.TestCheckResourceAttr(testResourceName, "description", "Acceptance Test"), + resource.TestCheckResourceAttr(testResourceName, "category", "LocalGatewayRules"), + resource.TestCheckResourceAttr(testResourceName, "comments", comments1), + resource.TestCheckResourceAttr(testResourceName, "locked", "true"), + resource.TestCheckResourceAttr(testResourceName, "sequence_number", "3"), + resource.TestCheckResourceAttr(testResourceName, "stateful", "true"), + resource.TestCheckResourceAttr(testResourceName, "tcp_strict", "false"), + resource.TestCheckResourceAttr(testResourceName, "rule.#", "0"), + resource.TestCheckResourceAttrSet(testResourceName, "revision"), + ), + }, + { + Config: testAccNsxtPolicyGatewayPolicyBasic(resourceName, updatedName, comments2, true), + Check: resource.ComposeTestCheckFunc( + testAccNsxtPolicyGatewayPolicyExists(testResourceName, defaultDomain), + resource.TestCheckResourceAttr(testResourceName, "display_name", updatedName), + resource.TestCheckResourceAttr(testResourceName, "description", "Acceptance Test"), + resource.TestCheckResourceAttr(testResourceName, "category", "LocalGatewayRules"), + resource.TestCheckResourceAttr(testResourceName, "comments", comments2), + resource.TestCheckResourceAttr(testResourceName, "locked", "true"), + resource.TestCheckResourceAttr(testResourceName, "sequence_number", "3"), + resource.TestCheckResourceAttr(testResourceName, "stateful", "true"), + resource.TestCheckResourceAttr(testResourceName, "tcp_strict", "false"), + resource.TestCheckResourceAttr(testResourceName, "rule.#", "0"), + ), + }, + { + Config: testAccNsxtVPCGatewayPolicyWithRule(resourceName, updatedName, direction1, proto1, tag1, true), + Check: resource.ComposeTestCheckFunc( + testAccNsxtPolicyGatewayPolicyExists(testResourceName, defaultDomain), + resource.TestCheckResourceAttr(testResourceName, "display_name", updatedName), + resource.TestCheckResourceAttr(testResourceName, "description", "Acceptance Test"), + resource.TestCheckResourceAttr(testResourceName, "category", "LocalGatewayRules"), + resource.TestCheckResourceAttr(testResourceName, "comments", ""), + resource.TestCheckResourceAttr(testResourceName, "locked", "false"), + resource.TestCheckResourceAttr(testResourceName, "sequence_number", "3"), + resource.TestCheckResourceAttr(testResourceName, "stateful", "true"), + resource.TestCheckResourceAttr(testResourceName, "tcp_strict", "false"), + resource.TestCheckResourceAttr(testResourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(testResourceName, "rule.0.display_name", updatedName), + resource.TestCheckResourceAttr(testResourceName, "rule.0.direction", direction1), + resource.TestCheckResourceAttr(testResourceName, "rule.0.ip_version", proto1), + resource.TestCheckResourceAttr(testResourceName, "rule.0.action", defaultAction), + resource.TestCheckResourceAttr(testResourceName, "rule.0.log_label", tag1), + resource.TestCheckResourceAttr(testResourceName, "rule.0.tag.#", "1"), + resource.TestCheckResourceAttrSet(testResourceName, "rule.0.rule_id"), + ), + }, + { + Config: testAccNsxtVPCGatewayPolicyWithRule(resourceName, updatedName, direction2, proto2, tag2, true), + Check: resource.ComposeTestCheckFunc( + testAccNsxtPolicyGatewayPolicyExists(testResourceName, defaultDomain), + resource.TestCheckResourceAttr(testResourceName, "display_name", updatedName), + resource.TestCheckResourceAttr(testResourceName, "description", "Acceptance Test"), + resource.TestCheckResourceAttr(testResourceName, "category", "LocalGatewayRules"), + resource.TestCheckResourceAttr(testResourceName, "comments", ""), + resource.TestCheckResourceAttr(testResourceName, "locked", "false"), + resource.TestCheckResourceAttr(testResourceName, "sequence_number", "3"), + resource.TestCheckResourceAttr(testResourceName, "stateful", "true"), + resource.TestCheckResourceAttr(testResourceName, "tcp_strict", "false"), + resource.TestCheckResourceAttr(testResourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(testResourceName, "rule.0.display_name", updatedName), + resource.TestCheckResourceAttr(testResourceName, "rule.0.direction", direction2), + resource.TestCheckResourceAttr(testResourceName, "rule.0.ip_version", proto2), + resource.TestCheckResourceAttr(testResourceName, "rule.0.action", defaultAction), + resource.TestCheckResourceAttr(testResourceName, "rule.0.log_label", tag2), + resource.TestCheckResourceAttr(testResourceName, "rule.0.tag.#", "1"), + ), + }, + }, + }) +} + +func TestAccResourceNsxtVPCGatewayPolicy_importBasic(t *testing.T) { + name := getAccTestResourceName() + resourceName := "nsxt_vpc_gateway_policy" + 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 testAccNsxtPolicyGatewayPolicyCheckDestroy(state, name, defaultDomain) + }, + Steps: []resource.TestStep{ + { + Config: testAccNsxtPolicyGatewayPolicyBasic(resourceName, name, "import", true), + }, + { + ResourceName: testResourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccResourceNsxtPolicyImportIDRetriever(testResourceName), + }, + }, + }) +} + +func testAccNsxtVPCGatewayPolicyWithRule(resourceName, name, direction, protocol, ruleTag string, withContext bool) string { + context := "" + if withContext { + context = testAccNsxtPolicyMultitenancyContext() + } + return fmt.Sprintf(` +resource "%s" "test" { +%s + display_name = "%s" + description = "Acceptance Test" + category = "LocalGatewayRules" + locked = false + sequence_number = 3 + stateful = true + tcp_strict = false + + tag { + scope = "color" + tag = "orange" + } + + rule { + display_name = "%s" + direction = "%s" + ip_version = "%s" + log_label = "%s" + + tag { + scope = "color" + tag = "blue" + } + } +}`, resourceName, context, name, name, direction, protocol, ruleTag) +} diff --git a/website/docs/r/vpc_gateway_policy.html.markdown b/website/docs/r/vpc_gateway_policy.html.markdown new file mode 100644 index 000000000..858092fb6 --- /dev/null +++ b/website/docs/r/vpc_gateway_policy.html.markdown @@ -0,0 +1,123 @@ +--- +subcategory: "VPC" +layout: "nsxt" +page_title: "NSXT: nsxt_vpc_gateway_policy" +description: A resource for VPC Gateway security policies. +--- + +# nsxt_vpc_gateway_policy + +This resource provides a method for the management of a VPC Gateway Policy and its Rules. + +This resource is applicable to NSX Policy Manager. + +## Example Usage + +## Example Usage + +```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_gateway_policy" "test" { + context { + project_id = data.nsxt_policy_project.demoproj.id + vpc_id = data.nsxt_policy_vpc.demovpc.id + } + display_name = "tf-gw-policy" + description = "Terraform provisioned Gateway Policy" + category = "LocalGatewayRules" + locked = false + sequence_number = 3 + stateful = true + tcp_strict = false + + tag { + scope = "color" + tag = "orange" + } + + rule { + display_name = "rule1" + destination_groups = [nsxt_vpc_group.group1.path, nsxt_vpc_group.group2.path] + disabled = true + action = "DROP" + logged = true + } + + lifecycle { + create_before_destroy = true + } +} +``` + +-> We recommend using `lifecycle` directive as in samples above, in order to avoid dependency issues when updating groups/services simultaneously with the rule. + +## Argument Reference + +The following arguments are supported: + +* `display_name` - (Required) Display name of the resource. +* `category` - (Required) The category to use for priority of this Gateway Policy. For local manager must be one of: `Emergency`, `SystemRules`, `SharedPreRules`, `LocalGatewayRules`, `AutoServiceRules` and `Default`. For global manager must be `SharedPreRules` or `LocalGatewayRules`. +* `description` - (Optional) Description of the resource. +* `tag` - (Optional) A list of scope + tag pairs to associate with this Gateway Policy. +* `nsx_id` - (Optional) The NSX ID of this resource. If set, this ID will be used to create the Gateway Policy 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 +* `comments` - (Optional) Comments for this Gateway Policy including lock/unlock comments. +* `locked` - (Optional) A boolean value indicating if the policy is locked. If locked, no other users can update the resource. +* `sequence_number` - (Optional) An int value used to resolve conflicts between security policies +* `stateful` - (Optional) A boolean value to indicate if this Policy is stateful. When it is stateful, the state of the network connects are tracked and a stateful packet inspection is performed. +* `tcp_strict` - (Optional) A boolean value to enable/disable a 3 way TCP handshake is done before the data packets are sent. +* `rule` (Optional) A repeatable block to specify rules for the Gateway Policy. Each rule includes the following fields: + * `display_name` - (Required) Display name of the resource. + * `description` - (Optional) Description of the resource. + * `destination_groups` - (Optional) Set of group paths that serve as the destination for this rule. IPs, IP ranges, or CIDRs. An empty set can be used to specify "Any". + * `destinations_excluded` - (Optional) A boolean value indicating negation of destination groups. + * `direction` - (Optional) The traffic direction for the policy. Must be one of: `IN`, `OUT` or `IN_OUT`. Defaults to `IN_OUT`. + * `disabled` - (Optional) A boolean value to indicate the rule is disabled. Defaults to `false`. + * `ip_version` - (Optional) The IP Protocol for the rule. Must be one of: `IPV4`, `IPV6` or `IPV4_IPV6`. Defaults to `IPV4_IPV6`. + * `logged` - (Optional) A boolean flag to enable packet logging. + * `notes` - (Optional) Text for additional notes on changes for the rule. + * `profiles` - (Optional) A list of context profiles for the rule. + * `services` - (Optional) List of services to match. + * `source_groups` - (Optional) Set of group paths that serve as the source for this rule. IPs, IP ranges, or CIDRs. An empty set can be used to specify "Any". + * `source_excluded` - (Optional) A boolean value indicating negation of source groups. + * `log_label` - (Optional) Additional information (string) which will be propagated to the rule syslog. + * `tag` - (Optional) A list of scope + tag pairs to associate with this Rule. + * `action` - (Optional) The action for the Rule. Must be one of: `ALLOW`, `DROP` or `REJECT`. Defaults to `ALLOW`. + * `sequence_number` - (Optional) It is recommended not to specify sequence number for rules, but rather rely on provider to auto-assign them. If you choose to specify sequence numbers, you must make sure the numbers are consistent with order of the rules in configuration. Please note that sequence numbers should start with 1, not 0. To avoid confusion, either specify sequence numbers in all rules, or none at all. + +## Attributes Reference + +In addition to arguments listed above, the following attributes are exported: + +* `id` - ID of the Security Policy. +* `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. +* `rule`: + * `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. + * `sequence_number` - Sequence number for the rule. + * `rule_id` - Unique positive number that is assigned by the system and is useful for debugging. + +## Importing + +An existing VPC Gateway Policy can be [imported][docs-import] into this resource, via the following command: + +[docs-import]: https://www.terraform.io/cli/import + +``` +terraform import nsxt_vpc_gateway_policy.policy1 PATH +``` + +The above command imports the VPC gateway policy named `policy1` with the NSX Policy path `PATH`.