diff --git a/docs/resources/metaport.md b/docs/resources/metaport.md index f7e91cd8..e04f4b4e 100644 --- a/docs/resources/metaport.md +++ b/docs/resources/metaport.md @@ -10,6 +10,10 @@ description: |- MetaPort is a lightweight virtual appliance that enables the secure authenticated interface interact between existing servers and the Proofpoint NaaS cloud. Once configured, metaports enable users to access your applications via the Proofpoint cloud. +~> **NOTE:** When using the `mapped_elements` argument of this resource, the resource will take over the management of the MetaPort's mapped elements. +This argument is incompatible with other methods of managing MetaPort's mapped elements, such as `metaport_mapped_elements_attachment`. +Any attempt to manage MetaPort's mapped elements using different methods, will result in resource cycling and/or errors. + ## Example Usage ```terraform diff --git a/docs/resources/metaport_mapped_elements_attachment.md b/docs/resources/metaport_mapped_elements_attachment.md new file mode 100644 index 00000000..d21e919a --- /dev/null +++ b/docs/resources/metaport_mapped_elements_attachment.md @@ -0,0 +1,46 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "pfptmeta_metaport_mapped_elements_attachment Resource - terraform-provider-pfptmeta" +subcategory: "Network" +description: |- + Attaches mapped elements to metaport. +--- + +# pfptmeta_metaport_mapped_elements_attachment (Resource) + +Attaches mapped elements to metaport. + +~> **NOTE:** Having multiple **metaport_mapped_elements_attachment** resources in conjunction with the same MetaPort and mapped elements will result in erratic behavior! +~> **NOTE:** For any given MetaPort, this resource is incompatible with the `pfptmeta_metaport` +[resource](https://registry.terraform.io/providers/nsofnetworks/pfptmeta/latest/docs/resources/metaport) `mapped_elements` argument. +When using this argument and resource simulteneously, both of them will attempt to manage the MetaPort's mapped elements. As a result, Terraform will display a permanent difference. + +## Example Usage + +```terraform +resource "pfptmeta_network_element" "mapped-subnet" { + name = "mapped subnet name" + mapped_subnets = ["0.0.0.0/0"] +} + +resource "pfptmeta_metaport" "metaport" { + name = "metaport name" +} + +resource "pfptmeta_metaport_mapped_elements_attachment" "attachment" { + metaport_id = pfptmeta_metaport.metaport.id + mapped_elements = [pfptmeta_network_element.mapped-subnet.id] +} +``` + + +## Schema + +### Required + +- **mapped_elements** (Set of String) Mapped element IDs to be attached to the metaport (Mapped Subnet, Mapped Service or Enterprise DNS) +- **metaport_id** (String) + +### Read-Only + +- **id** (String) The ID of this resource. diff --git a/examples/resources/pfptmeta_metaport_mapped_elements_attachment/resource.tf b/examples/resources/pfptmeta_metaport_mapped_elements_attachment/resource.tf new file mode 100644 index 00000000..33d3894f --- /dev/null +++ b/examples/resources/pfptmeta_metaport_mapped_elements_attachment/resource.tf @@ -0,0 +1,13 @@ +resource "pfptmeta_network_element" "mapped-subnet" { + name = "mapped subnet name" + mapped_subnets = ["0.0.0.0/0"] +} + +resource "pfptmeta_metaport" "metaport" { + name = "metaport name" +} + +resource "pfptmeta_metaport_mapped_elements_attachment" "attachment" { + metaport_id = pfptmeta_metaport.metaport.id + mapped_elements = [pfptmeta_network_element.mapped-subnet.id] +} \ No newline at end of file diff --git a/internal/client/metaport.go b/internal/client/metaport.go index 2d52703c..e673cc49 100644 --- a/internal/client/metaport.go +++ b/internal/client/metaport.go @@ -101,3 +101,33 @@ func DeleteMetaport(ctx context.Context, c *Client, mID string) (*Metaport, erro } return parseMetaport(resp) } + +func AddMappedElementsToMetaport(ctx context.Context, c *Client, mID string, meIDs []string) (*Metaport, error) { + url := fmt.Sprintf("%s/%s/%s/add_mapped_elements", c.BaseURL, metaportEndpoint, mID) + body := make(map[string][]string) + body["mapped_elements"] = meIDs + jsonBody, err := json.Marshal(body) + if err != nil { + return nil, fmt.Errorf("could not convert mapped elements to json: %v", err) + } + resp, err := c.Post(ctx, url, bytes.NewReader(jsonBody)) + if err != nil { + return nil, err + } + return parseMetaport(resp) +} + +func RemoveMappedElementsFromMetaport(ctx context.Context, c *Client, mID string, meIDs []string) (*Metaport, error) { + url := fmt.Sprintf("%s/%s/%s/remove_mapped_elements", c.BaseURL, metaportEndpoint, mID) + body := make(map[string][]string) + body["mapped_elements"] = meIDs + jsonBody, err := json.Marshal(body) + if err != nil { + return nil, fmt.Errorf("could not convert mapped elements to json: %v", err) + } + resp, err := c.Post(ctx, url, bytes.NewReader(jsonBody)) + if err != nil { + return nil, err + } + return parseMetaport(resp) +} diff --git a/internal/provider/acc_tests/metaport_mapped_elements_attachment_test.go b/internal/provider/acc_tests/metaport_mapped_elements_attachment_test.go new file mode 100644 index 00000000..05d43a8b --- /dev/null +++ b/internal/provider/acc_tests/metaport_mapped_elements_attachment_test.go @@ -0,0 +1,80 @@ +package acc_tests + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "regexp" + "testing" +) + +const ( + metaportAttachmentDependencies = ` +resource "pfptmeta_network_element" "mapped-service" { + name = "mapped service" + mapped_service = "mapped.service.com" +} + +resource "pfptmeta_network_element" "mapped-service2" { + name = "mapped service" + mapped_service = "mapped.service2.com" +} + +resource "pfptmeta_metaport" "metaport" { + name = "metaport name1" +} +` + metaportAttachment1 = ` +resource "pfptmeta_metaport_mapped_elements_attachment" "attachment" { + metaport_id = pfptmeta_metaport.metaport.id + mapped_elements = [pfptmeta_network_element.mapped-service.id] +} +` + metaportAttachment2 = ` +resource "pfptmeta_metaport_mapped_elements_attachment" "attachment2" { + metaport_id = pfptmeta_metaport.metaport.id + mapped_elements = [pfptmeta_network_element.mapped-service2.id] +} +` + metaportDataSource = ` +data "pfptmeta_metaport" "metaport" { + id = pfptmeta_metaport.metaport.id +} +` +) + +func TestAccMetaportAttachment(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: validateResourceDestroyed("metaport", "v1/metaports"), + Steps: []resource.TestStep{ + { + Config: metaportAttachmentDependencies + metaportAttachment1 + metaportAttachment2, + Check: resource.ComposeTestCheckFunc( + resource.TestMatchResourceAttr( + "pfptmeta_metaport_mapped_elements_attachment.attachment", "metaport_id", regexp.MustCompile("^mp-.+$"), + ), + resource.TestCheckResourceAttrPair( + "pfptmeta_network_element.mapped-service", "id", + "pfptmeta_metaport_mapped_elements_attachment.attachment", "mapped_elements.0"), + resource.TestCheckResourceAttrPair( + "pfptmeta_network_element.mapped-service2", "id", + "pfptmeta_metaport_mapped_elements_attachment.attachment2", "mapped_elements.0"), + ), + }, + { + Config: metaportAttachmentDependencies + metaportAttachment2, + }, + { + Config: metaportAttachmentDependencies + metaportAttachment2 + metaportDataSource, + Check: resource.ComposeTestCheckFunc( + resource.TestMatchResourceAttr( + "data.pfptmeta_metaport.metaport", "id", regexp.MustCompile("^mp-.+$"), + ), + resource.TestCheckResourceAttrPair( + "pfptmeta_network_element.mapped-service2", "id", + "data.pfptmeta_metaport.metaport", "mapped_elements.0"), + ), + }, + }, + }) +} diff --git a/internal/provider/metaport/resource.go b/internal/provider/metaport/resource.go index e7555d5d..0fdaa14a 100644 --- a/internal/provider/metaport/resource.go +++ b/internal/provider/metaport/resource.go @@ -34,6 +34,7 @@ func Resource() *schema.Resource { Description: mappedElementsDesc, Type: schema.TypeSet, Optional: true, + Computed: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateDiagFunc: common.ValidateID(true, "ed", "ne")}, diff --git a/internal/provider/metaport_mapped_elements_attachment/common.go b/internal/provider/metaport_mapped_elements_attachment/common.go new file mode 100644 index 00000000..8555ccd1 --- /dev/null +++ b/internal/provider/metaport_mapped_elements_attachment/common.go @@ -0,0 +1,79 @@ +package metaport_mapped_elements_attachment + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/nsofnetworks/terraform-provider-pfptmeta/internal/client" + "net/http" +) + +func generateID(mID string, meIDs []string) string { + hash := 0 + for _, meID := range meIDs { + hash += schema.HashString(meID) + } + return fmt.Sprintf("%s-%d", mID, hash) +} + +func attachmentToResource(d *schema.ResourceData, m *client.Metaport) (diags diag.Diagnostics) { + err := d.Set("metaport_id", m.ID) + if err != nil { + return diag.FromErr(err) + } + mMes := &schema.Set{F: schema.HashString} + for _, i := range m.MappedElements { + mMes.Add(i) + } + schemaMes := d.Get("mapped_elements").(*schema.Set) + schemaMesSet := schema.NewSet(schema.HashString, schemaMes.List()) + intersection := mMes.Intersection(schemaMesSet) + mes := client.ResourceTypeSetToStringSlice(intersection) + err = d.Set("mapped_elements", mes) + if err != nil { + return diag.FromErr(err) + } + d.SetId(generateID(m.ID, mes)) + return +} + +func readResource(ctx context.Context, d *schema.ResourceData, meta interface{}) (diags diag.Diagnostics) { + c := meta.(*client.Client) + + mID := d.Get("metaport_id").(string) + rg, err := client.GetMetaport(ctx, c, mID) + if err != nil { + errResponse, ok := err.(*client.ErrorResponse) + if ok && errResponse.Status == http.StatusNotFound { + d.SetId("") + } + return diag.FromErr(err) + } + return attachmentToResource(d, rg) +} +func createResource(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + c := meta.(*client.Client) + + mID := d.Get("metaport_id").(string) + mes := client.ResourceTypeSetToStringSlice(d.Get("mapped_elements").(*schema.Set)) + m, err := client.AddMappedElementsToMetaport(ctx, c, mID, mes) + if err != nil { + return diag.FromErr(err) + } + return attachmentToResource(d, m) +} + +func deleteResource(ctx context.Context, d *schema.ResourceData, meta interface{}) (diags diag.Diagnostics) { + c := meta.(*client.Client) + + mID := d.Get("metaport_id").(string) + mes := d.Get("mapped_elements").(*schema.Set) + _, err := client.RemoveMappedElementsFromMetaport(ctx, c, mID, client.ResourceTypeSetToStringSlice(mes)) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + d.SetId("") + return +} diff --git a/internal/provider/metaport_mapped_elements_attachment/resource.go b/internal/provider/metaport_mapped_elements_attachment/resource.go new file mode 100644 index 00000000..37e14b49 --- /dev/null +++ b/internal/provider/metaport_mapped_elements_attachment/resource.go @@ -0,0 +1,38 @@ +package metaport_mapped_elements_attachment + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/nsofnetworks/terraform-provider-pfptmeta/internal/provider/common" +) + +func Resource() *schema.Resource { + return &schema.Resource{ + Description: "Attaches mapped elements to metaport.", + ReadContext: readResource, + CreateContext: createResource, + DeleteContext: deleteResource, + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "metaport_id": { + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: common.ValidateID(false, "mp"), + ForceNew: true, + }, + "mapped_elements": { + Description: "Mapped element IDs to be attached to the metaport (Mapped Subnet, Mapped Service or Enterprise DNS)", + Required: true, + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateDiagFunc: common.ValidateID(true, "ne", "ed"), + }, + MinItems: 1, + ForceNew: true, + }, + }, + } +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 1c4c43f6..817f6bff 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -21,6 +21,7 @@ import ( "github.com/nsofnetworks/terraform-provider-pfptmeta/internal/provider/metaport" "github.com/nsofnetworks/terraform-provider-pfptmeta/internal/provider/metaport_cluster" "github.com/nsofnetworks/terraform-provider-pfptmeta/internal/provider/metaport_failover" + "github.com/nsofnetworks/terraform-provider-pfptmeta/internal/provider/metaport_mapped_elements_attachment" "github.com/nsofnetworks/terraform-provider-pfptmeta/internal/provider/network_element" "github.com/nsofnetworks/terraform-provider-pfptmeta/internal/provider/network_element_alias" "github.com/nsofnetworks/terraform-provider-pfptmeta/internal/provider/notification_channel" @@ -89,6 +90,7 @@ func New(version string) func() *schema.Provider { "pfptmeta_mapped_domain": mapped_domain.Resource(), "pfptmeta_mapped_host": mapped_host.Resource(), "pfptmeta_metaport": metaport.Resource(), + "pfptmeta_metaport_mapped_elements_attachment": metaport_mapped_elements_attachment.Resource(), "pfptmeta_metaport_cluster": metaport_cluster.Resource(), "pfptmeta_metaport_failover": metaport_failover.Resource(), "pfptmeta_enterprise_dns": enterprise_dns.Resource(), diff --git a/templates/resources/metaport.md.tmpl b/templates/resources/metaport.md.tmpl index 47838eeb..2f4b690b 100644 --- a/templates/resources/metaport.md.tmpl +++ b/templates/resources/metaport.md.tmpl @@ -10,6 +10,10 @@ description: |- {{ .Description | trimspace }} +~> **NOTE:** When using the `mapped_elements` argument of this resource, the resource will take over the management of the MetaPort's mapped elements. +This argument is incompatible with other methods of managing MetaPort's mapped elements, such as `metaport_mapped_elements_attachment`. +Any attempt to manage MetaPort's mapped elements using different methods, will result in resource cycling and/or errors. + ## Example Usage {{tffile "examples/resources/pfptmeta_metaport/resource.tf"}} diff --git a/templates/resources/metaport_mapped_elements_attachment.md.tmpl b/templates/resources/metaport_mapped_elements_attachment.md.tmpl new file mode 100644 index 00000000..fe496697 --- /dev/null +++ b/templates/resources/metaport_mapped_elements_attachment.md.tmpl @@ -0,0 +1,22 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "{{.Type}} {{.Name}} - {{.ProviderName}}" +subcategory: "Network" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Type}} ({{.Name}}) + +{{ .Description | trimspace }} + +~> **NOTE:** Having multiple **metaport_mapped_elements_attachment** resources in conjunction with the same MetaPort and mapped elements will result in erratic behavior! +~> **NOTE:** For any given MetaPort, this resource is incompatible with the `pfptmeta_metaport` +[resource](https://registry.terraform.io/providers/nsofnetworks/pfptmeta/latest/docs/resources/metaport) `mapped_elements` argument. +When using this argument and resource simulteneously, both of them will attempt to manage the MetaPort's mapped elements. As a result, Terraform will display a permanent difference. + +## Example Usage + +{{tffile "/examples/resources/pfptmeta_metaport_mapped_elements_attachment/resource.tf"}} + +{{ .SchemaMarkdown | trimspace }}