diff --git a/docs/resources/vpc_acl.md b/docs/resources/vpc_acl.md new file mode 100644 index 0000000000..8061933ba5 --- /dev/null +++ b/docs/resources/vpc_acl.md @@ -0,0 +1,70 @@ +--- +subcategory: "VPC" +page_title: "Scaleway: scaleway_vpc_acl" +--- + +# Resource: scaleway_vpc_acl + +Creates and manages Scaleway VPC ACLs. + +## Example Usage + +### Basic + +```terraform +resource "scaleway_vpc" "vpc01" { + name = "tf-vpc-acl" +} + +resource "scaleway_vpc_acl" "acl01" { + vpc_id = scaleway_vpc.vpc01.id + is_ipv6 = false + rules { + protocol = "TCP" + src_port_low = 0 + src_port_high = 0 + dst_port_low = 80 + dst_port_high = 80 + source = "0.0.0.0/0" + destination = "0.0.0.0/0" + description = "Allow HTTP traffic from any source" + action = "accept" + } + default_policy = "drop" +} +``` + +## Argument Reference + +The following arguments are supported: + +- `vpc_id` - (Required) The VPC ID the ACL belongs to. +- `default_policy` - (Required) The action to take for packets which do not match any rules. +- `is_ipv6` - (Optional) Defines whether this set of ACL rules is for IPv6 (false = IPv4). Each Network ACL can have rules for only one IP type. +- `rules` - (Optional) The list of Network ACL rules. + - `protocol` - (Optional) The protocol to which this rule applies. Default value: ANY. + - `source` - (Optional) The Source IP range to which this rule applies (CIDR notation with subnet mask). + - `src_port_low` - (Optional) The starting port of the source port range to which this rule applies (inclusive). + - `src_port_high` - (Optional) The ending port of the source port range to which this rule applies (inclusive). + - `destination` - (Optional) The destination IP range to which this rule applies (CIDR notation with subnet mask). + - `dst_port_low` - (Optional) The starting port of the destination port range to which this rule applies (inclusive). + - `dst_port_high` - (Optional) The ending port of the destination port range to which this rule applies (inclusive). + - `action` - (Optional) The policy to apply to the packet. + - `description` - (Optional) The rule description. +- `region` - (Defaults to [provider](../index.md#region) `region`) The [region](../guides/regions_and_zones.md#regions) of the ACL. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +- `id` - The ID of the ACL. + +~> **Important:** ACLs' IDs are [regional](../guides/regions_and_zones.md#resource-ids), which means they are of the form `{region}/{id}`, e.g. `fr-par/11111111-1111-1111-1111-111111111111 + +## Import + +ACLs can be imported using `{region}/{id}`, e.g. + +```bash +terraform import scaleway_vpc_acl.main fr-par/11111111-1111-1111-1111-111111111111 +``` diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 7eef862503..4496f3f0af 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -226,6 +226,7 @@ func Provider(config *Config) plugin.ProviderFunc { "scaleway_tem_domain_validation": tem.ResourceDomainValidation(), "scaleway_tem_webhook": tem.ResourceWebhook(), "scaleway_vpc": vpc.ResourceVPC(), + "scaleway_vpc_acl": vpc.ResourceACL(), "scaleway_vpc_gateway_network": vpcgw.ResourceNetwork(), "scaleway_vpc_private_network": vpc.ResourcePrivateNetwork(), "scaleway_vpc_public_gateway": vpcgw.ResourcePublicGateway(), diff --git a/internal/services/vpc/acl.go b/internal/services/vpc/acl.go new file mode 100644 index 0000000000..25c220b642 --- /dev/null +++ b/internal/services/vpc/acl.go @@ -0,0 +1,211 @@ +package vpc + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/scaleway/scaleway-sdk-go/api/vpc/v2" + "github.com/scaleway/scaleway-sdk-go/scw" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/httperrors" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/locality" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/verify" +) + +func ResourceACL() *schema.Resource { + return &schema.Resource{ + CreateContext: ResourceVPCACLCreate, + ReadContext: ResourceVPCACLRead, + UpdateContext: ResourceVPCACLUpdate, + DeleteContext: ResourceVPCACLDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + SchemaVersion: 0, + Schema: map[string]*schema.Schema{ + "vpc_id": { + Type: schema.TypeString, + Required: true, + Description: "The VPC in which to create the ACL rule", + }, + "default_policy": { + Type: schema.TypeString, + Required: true, + Description: "The action to take for packets which do not match any rules", + ValidateDiagFunc: verify.ValidateEnum[vpc.Action](), + }, + "is_ipv6": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Defines whether this set of ACL rules is for IPv6 (false = IPv4). Each Network ACL can have rules for only one IP type", + }, + "rules": { + Type: schema.TypeList, + Required: true, + Description: "The list of Network ACL rules", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "protocol": { + Type: schema.TypeString, + Optional: true, + Default: "ANY", + Description: "The protocol to which this rule applies. Default value: ANY", + ValidateDiagFunc: verify.ValidateEnum[vpc.ACLRuleProtocol](), + }, + "source": { + Type: schema.TypeString, + Optional: true, + Description: "Source IP range to which this rule applies (CIDR notation with subnet mask)", + }, + "src_port_low": { + Type: schema.TypeInt, + Optional: true, + Description: "Starting port of the source port range to which this rule applies (inclusive)", + }, + "src_port_high": { + Type: schema.TypeInt, + Optional: true, + Description: "Ending port of the source port range to which this rule applies (inclusive)", + }, + "destination": { + Type: schema.TypeString, + Optional: true, + Description: "Destination IP range to which this rule applies (CIDR notation with subnet mask)", + }, + "dst_port_low": { + Type: schema.TypeInt, + Optional: true, + Description: "Starting port of the destination port range to which this rule applies (inclusive)", + }, + "dst_port_high": { + Type: schema.TypeInt, + Optional: true, + Description: "Ending port of the destination port range to which this rule applies (inclusive)", + }, + "action": { + Type: schema.TypeString, + Optional: true, + Description: "The policy to apply to the packet", + ValidateDiagFunc: verify.ValidateEnum[vpc.Action](), + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: "The rule description", + }, + }, + }, + }, + "region": regional.Schema(), + }, + } +} + +func ResourceVPCACLCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + vpcAPI, region, err := vpcAPIWithRegion(d, m) + if err != nil { + return diag.FromErr(err) + } + + req := &vpc.SetACLRequest{ + VpcID: locality.ExpandID(d.Get("vpc_id").(string)), + IsIPv6: d.Get("is_ipv6").(bool), + DefaultPolicy: vpc.Action(d.Get("default_policy").(string)), + Region: region, + } + + expandedRules, err := expandACLRules(d.Get("rules")) + if err != nil { + return diag.FromErr(err) + } + + if d.Get("rules") != nil { + req.Rules = expandedRules + } + + _, err = vpcAPI.SetACL(req, scw.WithContext(ctx)) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(regional.NewIDString(region, regional.ExpandID(d.Get("vpc_id").(string)).ID)) + + return ResourceVPCACLRead(ctx, d, m) +} + +func ResourceVPCACLRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + vpcAPI, region, ID, err := NewAPIWithRegionAndID(m, d.Id()) + if err != nil { + return diag.FromErr(err) + } + + acl, err := vpcAPI.GetACL(&vpc.GetACLRequest{ + VpcID: locality.ExpandID(ID), + Region: region, + IsIPv6: d.Get("is_ipv6").(bool), + }, scw.WithContext(ctx)) + if err != nil { + if httperrors.Is404(err) { + d.SetId("") + + return nil + } + + return diag.FromErr(err) + } + + _ = d.Set("rules", flattenACLRules(acl.Rules)) + _ = d.Set("default_policy", acl.DefaultPolicy.String()) + + return nil +} + +func ResourceVPCACLUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + vpcAPI, region, ID, err := NewAPIWithRegionAndID(m, d.Id()) + if err != nil { + return diag.FromErr(err) + } + + req := &vpc.SetACLRequest{ + VpcID: locality.ExpandID(ID), + IsIPv6: d.Get("is_ipv6").(bool), + DefaultPolicy: vpc.Action(d.Get("default_policy").(string)), + Region: region, + } + + expandedRules, err := expandACLRules(d.Get("rules")) + if err != nil { + return diag.FromErr(err) + } + + if d.Get("rules") != nil { + req.Rules = expandedRules + } + + _, err = vpcAPI.SetACL(req, scw.WithContext(ctx)) + if err != nil { + return diag.FromErr(err) + } + + return ResourceVPCACLRead(ctx, d, m) +} + +func ResourceVPCACLDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + vpcAPI, region, ID, err := NewAPIWithRegionAndID(m, d.Id()) + if err != nil { + return diag.FromErr(err) + } + + _, err = vpcAPI.SetACL(&vpc.SetACLRequest{ + VpcID: locality.ExpandID(ID), + Region: region, + DefaultPolicy: "drop", + }, scw.WithContext(ctx)) + if err != nil { + return diag.FromErr(err) + } + + return nil +} diff --git a/internal/services/vpc/acl_test.go b/internal/services/vpc/acl_test.go new file mode 100644 index 0000000000..b7ff523563 --- /dev/null +++ b/internal/services/vpc/acl_test.go @@ -0,0 +1,180 @@ +package vpc_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + vpcSDK "github.com/scaleway/scaleway-sdk-go/api/vpc/v2" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/httperrors" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/services/vpc" +) + +func TestAccACL_Basic(t *testing.T) { + tt := acctest.NewTestTools(t) + defer tt.Cleanup() + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProviderFactories: tt.ProviderFactories, + CheckDestroy: isACLDestroyed(tt), + Steps: []resource.TestStep{ + { + Config: ` + resource "scaleway_vpc" "vpc01" { + name = "tf-vpc-acl" + } + + resource "scaleway_vpc_acl" "acl01" { + vpc_id = scaleway_vpc.vpc01.id + is_ipv6 = false + rules { + protocol = "TCP" + src_port_low = 0 + src_port_high = 0 + dst_port_low = 80 + dst_port_high = 80 + source = "0.0.0.0/0" + destination = "0.0.0.0/0" + description = "Allow HTTP traffic from any source" + action = "accept" + } + default_policy = "drop" + } + `, + Check: resource.ComposeTestCheckFunc( + isACLPresent(tt, "scaleway_vpc_acl.acl01"), + resource.TestCheckResourceAttrPair("scaleway_vpc_acl.acl01", "vpc_id", "scaleway_vpc.vpc01", "id"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "is_ipv6", "false"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "default_policy", "drop"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.#", "1"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.protocol", "TCP"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.src_port_low", "0"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.src_port_high", "0"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.dst_port_low", "80"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.dst_port_high", "80"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.source", "0.0.0.0/0"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.destination", "0.0.0.0/0"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.description", "Allow HTTP traffic from any source"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.action", "accept"), + ), + }, + { + Config: ` + resource "scaleway_vpc" "vpc01" { + name = "tf-vpc-acl" + } + + resource "scaleway_vpc_acl" "acl01" { + vpc_id = scaleway_vpc.vpc01.id + is_ipv6 = false + rules { + protocol = "TCP" + src_port_low = 0 + src_port_high = 0 + dst_port_low = 80 + dst_port_high = 80 + source = "0.0.0.0/0" + destination = "0.0.0.0/0" + description = "Allow HTTP traffic from any source" + action = "accept" + } + rules { + protocol = "TCP" + src_port_low = 0 + src_port_high = 0 + dst_port_low = 443 + dst_port_high = 443 + source = "0.0.0.0/0" + destination = "0.0.0.0/0" + description = "Allow HTTPS traffic from any source" + action = "accept" + } + default_policy = "drop" + } + + `, + Check: resource.ComposeTestCheckFunc( + isACLPresent(tt, "scaleway_vpc_acl.acl01"), + resource.TestCheckResourceAttrPair("scaleway_vpc_acl.acl01", "vpc_id", "scaleway_vpc.vpc01", "id"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "is_ipv6", "false"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "default_policy", "drop"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.#", "2"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.protocol", "TCP"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.src_port_low", "0"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.src_port_high", "0"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.dst_port_low", "80"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.dst_port_high", "80"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.source", "0.0.0.0/0"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.destination", "0.0.0.0/0"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.description", "Allow HTTP traffic from any source"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.0.action", "accept"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.1.protocol", "TCP"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.1.src_port_low", "0"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.1.src_port_high", "0"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.1.dst_port_low", "443"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.1.dst_port_high", "443"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.1.source", "0.0.0.0/0"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.1.destination", "0.0.0.0/0"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.1.description", "Allow HTTPS traffic from any source"), + resource.TestCheckResourceAttr("scaleway_vpc_acl.acl01", "rules.1.action", "accept"), + ), + }, + }, + }) +} + +func isACLPresent(tt *acctest.TestTools, n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("resource not found: %s", n) + } + + vpcAPI, region, ID, err := vpc.NewAPIWithRegionAndID(tt.Meta, rs.Primary.ID) + if err != nil { + return err + } + + _, err = vpcAPI.GetACL(&vpcSDK.GetACLRequest{ + VpcID: ID, + Region: region, + }) + if err != nil { + return err + } + + return nil + } +} + +func isACLDestroyed(tt *acctest.TestTools) resource.TestCheckFunc { + return func(state *terraform.State) error { + for _, rs := range state.RootModule().Resources { + if rs.Type != "scaleway_vpc_acl" { + continue + } + + vpcAPI, region, ID, err := vpc.NewAPIWithRegionAndID(tt.Meta, rs.Primary.ID) + if err != nil { + return err + } + + _, err = vpcAPI.GetACL(&vpcSDK.GetACLRequest{ + VpcID: ID, + Region: region, + }) + + if err == nil { + return fmt.Errorf("acl (%s) still exists", rs.Primary.ID) + } + + if !httperrors.Is404(err) { + return err + } + } + + return nil + } +} diff --git a/internal/services/vpc/testdata/acl-basic.cassette.yaml b/internal/services/vpc/testdata/acl-basic.cassette.yaml new file mode 100644 index 0000000000..096dc4751d --- /dev/null +++ b/internal/services/vpc/testdata/acl-basic.cassette.yaml @@ -0,0 +1,842 @@ +--- +version: 2 +interactions: + - id: 0 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 106 + transfer_encoding: [] + trailer: {} + host: api.scaleway.com + remote_addr: "" + request_uri: "" + body: '{"name":"tf-vpc-acl","project_id":"564aa517-68b0-4fd7-8c8c-d21c4bcdcbd5","tags":[],"enable_routing":false}' + form: {} + headers: + Content-Type: + - application/json + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.0; darwin; amd64) terraform-provider/develop terraform/terraform-tests + url: https://api.scaleway.com/vpc/v2/regions/fr-par/vpcs + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 356 + uncompressed: false + body: '{"created_at":"2025-03-24T16:10:44.453250Z","id":"3a88a217-657d-41c4-bf1e-51a76be9f4f0","is_default":false,"name":"tf-vpc-acl","organization_id":"564aa517-68b0-4fd7-8c8c-d21c4bcdcbd5","private_network_count":0,"project_id":"564aa517-68b0-4fd7-8c8c-d21c4bcdcbd5","region":"fr-par","routing_enabled":true,"tags":[],"updated_at":"2025-03-24T16:10:44.453250Z"}' + headers: + Content-Length: + - "356" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 24 Mar 2025 16:10:44 GMT + Server: + - Scaleway API Gateway (fr-par-3;edge02) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 24c2da62-47c1-471d-ba68-0f65d9e72479 + status: 200 OK + code: 200 + duration: 740.018121ms + - id: 1 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.scaleway.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.0; darwin; amd64) terraform-provider/develop terraform/terraform-tests + url: https://api.scaleway.com/vpc/v2/regions/fr-par/vpcs/3a88a217-657d-41c4-bf1e-51a76be9f4f0 + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 356 + uncompressed: false + body: '{"created_at":"2025-03-24T16:10:44.453250Z","id":"3a88a217-657d-41c4-bf1e-51a76be9f4f0","is_default":false,"name":"tf-vpc-acl","organization_id":"564aa517-68b0-4fd7-8c8c-d21c4bcdcbd5","private_network_count":0,"project_id":"564aa517-68b0-4fd7-8c8c-d21c4bcdcbd5","region":"fr-par","routing_enabled":true,"tags":[],"updated_at":"2025-03-24T16:10:44.453250Z"}' + headers: + Content-Length: + - "356" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 24 Mar 2025 16:10:44 GMT + Server: + - Scaleway API Gateway (fr-par-3;edge02) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 192b6e56-1e2e-486d-a0c4-6d83dedd6045 + status: 200 OK + code: 200 + duration: 209.410149ms + - id: 2 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 258 + transfer_encoding: [] + trailer: {} + host: api.scaleway.com + remote_addr: "" + request_uri: "" + body: '{"rules":[{"protocol":"TCP","source":"0.0.0.0/0","src_port_low":0,"src_port_high":0,"destination":"0.0.0.0/0","dst_port_low":80,"dst_port_high":80,"action":"accept","description":"Allow HTTP traffic from any source"}],"is_ipv6":false,"default_policy":"drop"}' + form: {} + headers: + Content-Type: + - application/json + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.0; darwin; amd64) terraform-provider/develop terraform/terraform-tests + url: https://api.scaleway.com/vpc/v2/regions/fr-par/vpcs/3a88a217-657d-41c4-bf1e-51a76be9f4f0/acl-rules + method: PUT + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 242 + uncompressed: false + body: '{"default_policy":"drop","rules":[{"action":"accept","description":"Allow HTTP traffic from any source","destination":"0.0.0.0/0","dst_port_high":80,"dst_port_low":80,"protocol":"TCP","source":"0.0.0.0/0","src_port_high":0,"src_port_low":0}]}' + headers: + Content-Length: + - "242" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 24 Mar 2025 16:10:45 GMT + Server: + - Scaleway API Gateway (fr-par-3;edge02) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - b8af4b5c-1618-4ffa-b688-88864c29fcab + status: 200 OK + code: 200 + duration: 171.074697ms + - id: 3 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.scaleway.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.0; darwin; amd64) terraform-provider/develop terraform/terraform-tests + url: https://api.scaleway.com/vpc/v2/regions/fr-par/vpcs/3a88a217-657d-41c4-bf1e-51a76be9f4f0/acl-rules?is_ipv6=false + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 242 + uncompressed: false + body: '{"default_policy":"drop","rules":[{"action":"accept","description":"Allow HTTP traffic from any source","destination":"0.0.0.0/0","dst_port_high":80,"dst_port_low":80,"protocol":"TCP","source":"0.0.0.0/0","src_port_high":0,"src_port_low":0}]}' + headers: + Content-Length: + - "242" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 24 Mar 2025 16:10:45 GMT + Server: + - Scaleway API Gateway (fr-par-3;edge02) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - becea538-f5b4-4220-97c8-2606d254e782 + status: 200 OK + code: 200 + duration: 179.300401ms + - id: 4 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.scaleway.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.0; darwin; amd64) terraform-provider/develop terraform/terraform-tests + url: https://api.scaleway.com/vpc/v2/regions/fr-par/vpcs/3a88a217-657d-41c4-bf1e-51a76be9f4f0/acl-rules?is_ipv6=false + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 242 + uncompressed: false + body: '{"default_policy":"drop","rules":[{"action":"accept","description":"Allow HTTP traffic from any source","destination":"0.0.0.0/0","dst_port_high":80,"dst_port_low":80,"protocol":"TCP","source":"0.0.0.0/0","src_port_high":0,"src_port_low":0}]}' + headers: + Content-Length: + - "242" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 24 Mar 2025 16:10:45 GMT + Server: + - Scaleway API Gateway (fr-par-3;edge02) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 4b0d21e4-0e09-495f-a528-cacaad39054a + status: 200 OK + code: 200 + duration: 163.902964ms + - id: 5 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.scaleway.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.0; darwin; amd64) terraform-provider/develop terraform/terraform-tests + url: https://api.scaleway.com/vpc/v2/regions/fr-par/vpcs/3a88a217-657d-41c4-bf1e-51a76be9f4f0 + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 356 + uncompressed: false + body: '{"created_at":"2025-03-24T16:10:44.453250Z","id":"3a88a217-657d-41c4-bf1e-51a76be9f4f0","is_default":false,"name":"tf-vpc-acl","organization_id":"564aa517-68b0-4fd7-8c8c-d21c4bcdcbd5","private_network_count":0,"project_id":"564aa517-68b0-4fd7-8c8c-d21c4bcdcbd5","region":"fr-par","routing_enabled":true,"tags":[],"updated_at":"2025-03-24T16:10:44.985811Z"}' + headers: + Content-Length: + - "356" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 24 Mar 2025 16:10:46 GMT + Server: + - Scaleway API Gateway (fr-par-3;edge02) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - fe47e17f-0de2-4455-ba1f-31c94ee0cacb + status: 200 OK + code: 200 + duration: 186.670582ms + - id: 6 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.scaleway.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.0; darwin; amd64) terraform-provider/develop terraform/terraform-tests + url: https://api.scaleway.com/vpc/v2/regions/fr-par/vpcs/3a88a217-657d-41c4-bf1e-51a76be9f4f0/acl-rules?is_ipv6=false + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 242 + uncompressed: false + body: '{"default_policy":"drop","rules":[{"action":"accept","description":"Allow HTTP traffic from any source","destination":"0.0.0.0/0","dst_port_high":80,"dst_port_low":80,"protocol":"TCP","source":"0.0.0.0/0","src_port_high":0,"src_port_low":0}]}' + headers: + Content-Length: + - "242" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 24 Mar 2025 16:10:46 GMT + Server: + - Scaleway API Gateway (fr-par-3;edge02) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 43041a1a-05c5-4de4-ac01-704559b714df + status: 200 OK + code: 200 + duration: 196.721564ms + - id: 7 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.scaleway.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.0; darwin; amd64) terraform-provider/develop terraform/terraform-tests + url: https://api.scaleway.com/vpc/v2/regions/fr-par/vpcs/3a88a217-657d-41c4-bf1e-51a76be9f4f0 + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 356 + uncompressed: false + body: '{"created_at":"2025-03-24T16:10:44.453250Z","id":"3a88a217-657d-41c4-bf1e-51a76be9f4f0","is_default":false,"name":"tf-vpc-acl","organization_id":"564aa517-68b0-4fd7-8c8c-d21c4bcdcbd5","private_network_count":0,"project_id":"564aa517-68b0-4fd7-8c8c-d21c4bcdcbd5","region":"fr-par","routing_enabled":true,"tags":[],"updated_at":"2025-03-24T16:10:44.985811Z"}' + headers: + Content-Length: + - "356" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 24 Mar 2025 16:10:46 GMT + Server: + - Scaleway API Gateway (fr-par-3;edge02) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - e6a0e0f3-6e63-41c0-86b4-984629a3bd48 + status: 200 OK + code: 200 + duration: 79.123985ms + - id: 8 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.scaleway.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.0; darwin; amd64) terraform-provider/develop terraform/terraform-tests + url: https://api.scaleway.com/vpc/v2/regions/fr-par/vpcs/3a88a217-657d-41c4-bf1e-51a76be9f4f0/acl-rules?is_ipv6=false + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 242 + uncompressed: false + body: '{"default_policy":"drop","rules":[{"action":"accept","description":"Allow HTTP traffic from any source","destination":"0.0.0.0/0","dst_port_high":80,"dst_port_low":80,"protocol":"TCP","source":"0.0.0.0/0","src_port_high":0,"src_port_low":0}]}' + headers: + Content-Length: + - "242" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 24 Mar 2025 16:10:47 GMT + Server: + - Scaleway API Gateway (fr-par-3;edge02) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 4d672b43-35be-460e-8d58-2ae61ecef1fa + status: 200 OK + code: 200 + duration: 85.192684ms + - id: 9 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 468 + transfer_encoding: [] + trailer: {} + host: api.scaleway.com + remote_addr: "" + request_uri: "" + body: '{"rules":[{"protocol":"TCP","source":"0.0.0.0/0","src_port_low":0,"src_port_high":0,"destination":"0.0.0.0/0","dst_port_low":80,"dst_port_high":80,"action":"accept","description":"Allow HTTP traffic from any source"},{"protocol":"TCP","source":"0.0.0.0/0","src_port_low":0,"src_port_high":0,"destination":"0.0.0.0/0","dst_port_low":443,"dst_port_high":443,"action":"accept","description":"Allow HTTPS traffic from any source"}],"is_ipv6":false,"default_policy":"drop"}' + form: {} + headers: + Content-Type: + - application/json + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.0; darwin; amd64) terraform-provider/develop terraform/terraform-tests + url: https://api.scaleway.com/vpc/v2/regions/fr-par/vpcs/3a88a217-657d-41c4-bf1e-51a76be9f4f0/acl-rules + method: PUT + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 452 + uncompressed: false + body: '{"default_policy":"drop","rules":[{"action":"accept","description":"Allow HTTP traffic from any source","destination":"0.0.0.0/0","dst_port_high":80,"dst_port_low":80,"protocol":"TCP","source":"0.0.0.0/0","src_port_high":0,"src_port_low":0},{"action":"accept","description":"Allow HTTPS traffic from any source","destination":"0.0.0.0/0","dst_port_high":443,"dst_port_low":443,"protocol":"TCP","source":"0.0.0.0/0","src_port_high":0,"src_port_low":0}]}' + headers: + Content-Length: + - "452" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 24 Mar 2025 16:10:47 GMT + Server: + - Scaleway API Gateway (fr-par-3;edge02) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 6f7d04ce-5cfa-4b42-993c-4f156b3ea69a + status: 200 OK + code: 200 + duration: 171.225243ms + - id: 10 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.scaleway.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.0; darwin; amd64) terraform-provider/develop terraform/terraform-tests + url: https://api.scaleway.com/vpc/v2/regions/fr-par/vpcs/3a88a217-657d-41c4-bf1e-51a76be9f4f0/acl-rules?is_ipv6=false + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 452 + uncompressed: false + body: '{"default_policy":"drop","rules":[{"action":"accept","description":"Allow HTTP traffic from any source","destination":"0.0.0.0/0","dst_port_high":80,"dst_port_low":80,"protocol":"TCP","source":"0.0.0.0/0","src_port_high":0,"src_port_low":0},{"action":"accept","description":"Allow HTTPS traffic from any source","destination":"0.0.0.0/0","dst_port_high":443,"dst_port_low":443,"protocol":"TCP","source":"0.0.0.0/0","src_port_high":0,"src_port_low":0}]}' + headers: + Content-Length: + - "452" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 24 Mar 2025 16:10:47 GMT + Server: + - Scaleway API Gateway (fr-par-3;edge02) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 9db71af2-ebda-4dba-b806-917d21ffc6ef + status: 200 OK + code: 200 + duration: 140.083578ms + - id: 11 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.scaleway.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.0; darwin; amd64) terraform-provider/develop terraform/terraform-tests + url: https://api.scaleway.com/vpc/v2/regions/fr-par/vpcs/3a88a217-657d-41c4-bf1e-51a76be9f4f0/acl-rules?is_ipv6=false + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 452 + uncompressed: false + body: '{"default_policy":"drop","rules":[{"action":"accept","description":"Allow HTTP traffic from any source","destination":"0.0.0.0/0","dst_port_high":80,"dst_port_low":80,"protocol":"TCP","source":"0.0.0.0/0","src_port_high":0,"src_port_low":0},{"action":"accept","description":"Allow HTTPS traffic from any source","destination":"0.0.0.0/0","dst_port_high":443,"dst_port_low":443,"protocol":"TCP","source":"0.0.0.0/0","src_port_high":0,"src_port_low":0}]}' + headers: + Content-Length: + - "452" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 24 Mar 2025 16:10:48 GMT + Server: + - Scaleway API Gateway (fr-par-3;edge02) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 02cd096d-4b9a-4df0-b2e5-3fe693b7de2a + status: 200 OK + code: 200 + duration: 198.763061ms + - id: 12 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.scaleway.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.0; darwin; amd64) terraform-provider/develop terraform/terraform-tests + url: https://api.scaleway.com/vpc/v2/regions/fr-par/vpcs/3a88a217-657d-41c4-bf1e-51a76be9f4f0 + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 356 + uncompressed: false + body: '{"created_at":"2025-03-24T16:10:44.453250Z","id":"3a88a217-657d-41c4-bf1e-51a76be9f4f0","is_default":false,"name":"tf-vpc-acl","organization_id":"564aa517-68b0-4fd7-8c8c-d21c4bcdcbd5","private_network_count":0,"project_id":"564aa517-68b0-4fd7-8c8c-d21c4bcdcbd5","region":"fr-par","routing_enabled":true,"tags":[],"updated_at":"2025-03-24T16:10:47.793135Z"}' + headers: + Content-Length: + - "356" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 24 Mar 2025 16:10:48 GMT + Server: + - Scaleway API Gateway (fr-par-3;edge02) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - d04b2e20-d50b-4937-a831-a8b99437f8c8 + status: 200 OK + code: 200 + duration: 108.920508ms + - id: 13 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.scaleway.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.0; darwin; amd64) terraform-provider/develop terraform/terraform-tests + url: https://api.scaleway.com/vpc/v2/regions/fr-par/vpcs/3a88a217-657d-41c4-bf1e-51a76be9f4f0/acl-rules?is_ipv6=false + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 452 + uncompressed: false + body: '{"default_policy":"drop","rules":[{"action":"accept","description":"Allow HTTP traffic from any source","destination":"0.0.0.0/0","dst_port_high":80,"dst_port_low":80,"protocol":"TCP","source":"0.0.0.0/0","src_port_high":0,"src_port_low":0},{"action":"accept","description":"Allow HTTPS traffic from any source","destination":"0.0.0.0/0","dst_port_high":443,"dst_port_low":443,"protocol":"TCP","source":"0.0.0.0/0","src_port_high":0,"src_port_low":0}]}' + headers: + Content-Length: + - "452" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 24 Mar 2025 16:10:49 GMT + Server: + - Scaleway API Gateway (fr-par-3;edge02) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - ca788787-e8c6-4c2b-a06d-6c62b1800aa5 + status: 200 OK + code: 200 + duration: 103.938102ms + - id: 14 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 54 + transfer_encoding: [] + trailer: {} + host: api.scaleway.com + remote_addr: "" + request_uri: "" + body: '{"rules":null,"is_ipv6":false,"default_policy":"drop"}' + form: {} + headers: + Content-Type: + - application/json + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.0; darwin; amd64) terraform-provider/develop terraform/terraform-tests + url: https://api.scaleway.com/vpc/v2/regions/fr-par/vpcs/3a88a217-657d-41c4-bf1e-51a76be9f4f0/acl-rules + method: PUT + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 36 + uncompressed: false + body: '{"default_policy":"drop","rules":[]}' + headers: + Content-Length: + - "36" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 24 Mar 2025 16:10:49 GMT + Server: + - Scaleway API Gateway (fr-par-3;edge02) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - e109e412-828f-4434-b147-0370f2535a37 + status: 200 OK + code: 200 + duration: 272.731323ms + - id: 15 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.scaleway.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.0; darwin; amd64) terraform-provider/develop terraform/terraform-tests + url: https://api.scaleway.com/vpc/v2/regions/fr-par/vpcs/3a88a217-657d-41c4-bf1e-51a76be9f4f0 + method: DELETE + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 0 + uncompressed: false + body: "" + headers: + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 24 Mar 2025 16:10:50 GMT + Server: + - Scaleway API Gateway (fr-par-3;edge02) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - ffc8be44-4a89-4ea5-9b5f-0b3f56417f35 + status: 204 No Content + code: 204 + duration: 289.981985ms + - id: 16 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.scaleway.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.0; darwin; amd64) terraform-provider/develop terraform/terraform-tests + url: https://api.scaleway.com/vpc/v2/regions/fr-par/vpcs/3a88a217-657d-41c4-bf1e-51a76be9f4f0/acl-rules?is_ipv6=false + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 124 + uncompressed: false + body: '{"message":"resource is not found","resource":"vpc","resource_id":"3a88a217-657d-41c4-bf1e-51a76be9f4f0","type":"not_found"}' + headers: + Content-Length: + - "124" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 24 Mar 2025 16:10:50 GMT + Server: + - Scaleway API Gateway (fr-par-3;edge02) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 79350def-5cc1-4da5-96b7-7e0a26422677 + status: 404 Not Found + code: 404 + duration: 215.892322ms diff --git a/internal/services/vpc/types.go b/internal/services/vpc/types.go index b2835f9836..0d2b8fc833 100644 --- a/internal/services/vpc/types.go +++ b/internal/services/vpc/types.go @@ -138,12 +138,84 @@ func flattenAndSortSubnetV2s(subnets []*vpc.Subnet) (interface{}, interface{}) { return flatIpv4Subnets, flatIpv6Subnets } +func expandACLRules(data interface{}) ([]*vpc.ACLRule, error) { + if data == nil { + return nil, nil + } + + rules := []*vpc.ACLRule(nil) + + for _, rule := range data.([]interface{}) { + rawRule := rule.(map[string]interface{}) + ACLRule := &vpc.ACLRule{} + + source, err := types.ExpandIPNet(rawRule["source"].(string)) + if err != nil { + return nil, err + } + + destination, err := types.ExpandIPNet(rawRule["destination"].(string)) + if err != nil { + return nil, err + } + + ACLRule.Protocol = vpc.ACLRuleProtocol(rawRule["protocol"].(string)) + ACLRule.Source = source + ACLRule.SrcPortLow = uint32(rawRule["src_port_low"].(int)) + ACLRule.SrcPortHigh = uint32(rawRule["src_port_high"].(int)) + ACLRule.Destination = destination + ACLRule.DstPortLow = uint32(rawRule["dst_port_low"].(int)) + ACLRule.DstPortHigh = uint32(rawRule["dst_port_high"].(int)) + ACLRule.Action = vpc.Action(rawRule["action"].(string)) + ACLRule.Description = types.ExpandStringPtr(rawRule["description"].(string)) + + rules = append(rules, ACLRule) + } + + return rules, nil +} + +func flattenACLRules(rules []*vpc.ACLRule) interface{} { + if rules == nil { + return nil + } + + flattenedRules := []map[string]interface{}(nil) + + for _, rule := range rules { + flattenedSource, err := types.FlattenIPNet(rule.Source) + if err != nil { + return nil + } + + flattenedDestination, err := types.FlattenIPNet(rule.Destination) + if err != nil { + return nil + } + + flattenedRules = append(flattenedRules, map[string]interface{}{ + "protocol": rule.Protocol.String(), + "source": flattenedSource, + "src_port_low": int(rule.SrcPortLow), + "src_port_high": int(rule.SrcPortHigh), + "destination": flattenedDestination, + "dst_port_low": int(rule.DstPortLow), + "dst_port_high": int(rule.DstPortHigh), + "action": rule.Action.String(), + "description": types.FlattenStringPtr(rule.Description), + }) + } + + return flattenedRules +} + func maskHexToDottedDecimal(mask net.IPMask) string { if len(mask) != net.IPv4len && len(mask) != net.IPv6len { return "" } parts := make([]string, len(mask)) + for i, part := range mask { parts[i] = strconv.Itoa(int(part)) }