Skip to content

Commit

Permalink
Merge pull request #29623 from GlennChia/f-aws_networkmanager_core_ne…
Browse files Browse the repository at this point in the history
…twork-base-policy-multi-region

r/aws_networkmanager_core_network - base policy multi region support
  • Loading branch information
jar-b authored Mar 2, 2023
2 parents 8a1333e + 52bb79b commit 859c562
Show file tree
Hide file tree
Showing 6 changed files with 468 additions and 22 deletions.
7 changes: 7 additions & 0 deletions .changelog/29623.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/aws_networkmanager_core_network: Add `base_policy_regions` argument
```

```release-note:note
resource/aws_networkmanager_core_network: The `base_policy_region` argument is being deprecated in favor of the new `base_policy_regions` argument.
```
78 changes: 64 additions & 14 deletions internal/service/networkmanager/core_network.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package networkmanager

import (
"context"
"encoding/json"
"fmt"
"log"
"time"
Expand Down Expand Up @@ -58,9 +59,21 @@ func ResourceCoreNetwork() *schema.Resource {
Computed: true,
},
"base_policy_region": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: verify.ValidRegionName,
Deprecated: "Use the base_policy_regions argument instead. " +
"This argument will be removed in the next major version of the provider.",
Type: schema.TypeString,
Optional: true,
ValidateFunc: verify.ValidRegionName,
ConflictsWith: []string{"base_policy_regions"},
},
"base_policy_regions": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: verify.ValidRegionName,
},
ConflictsWith: []string{"base_policy_region"},
},
"create_base_policy": {
Type: schema.TypeBool,
Expand Down Expand Up @@ -178,13 +191,18 @@ func resourceCoreNetworkCreate(ctx context.Context, d *schema.ResourceData, meta
// this is required for the first terraform apply if there attachments to the core network
// and the core network is created without the policy_document argument set
if _, ok := d.GetOk("create_base_policy"); ok {
// if user supplies a region use it in the base policy, otherwise use current region
region := meta.(*conns.AWSClient).Region
// if user supplies a region or multiple regions use it in the base policy, otherwise use current region
regions := []interface{}{meta.(*conns.AWSClient).Region}
if v, ok := d.GetOk("base_policy_region"); ok {
region = v.(string)
regions = []interface{}{v.(string)}
} else if v, ok := d.GetOk("base_policy_regions"); ok && v.(*schema.Set).Len() > 0 {
regions = v.(*schema.Set).List()
}

policyDocumentTarget := buildCoreNetworkBasePolicyDocument(region)
policyDocumentTarget, err := buildCoreNetworkBasePolicyDocument(regions)
if err != nil {
return diag.Errorf("Formatting Core Network Base Policy: %s", err)
}
input.PolicyDocument = aws.String(policyDocumentTarget)
}

Expand Down Expand Up @@ -304,14 +322,21 @@ func resourceCoreNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta

if d.HasChange("create_base_policy") {
if _, ok := d.GetOk("create_base_policy"); ok {
// if user supplies a region use it in the base policy, otherwise use current region
region := meta.(*conns.AWSClient).Region
// if user supplies a region or multiple regions use it in the base policy, otherwise use current region
regions := []interface{}{meta.(*conns.AWSClient).Region}
if v, ok := d.GetOk("base_policy_region"); ok {
region = v.(string)
regions = []interface{}{v.(string)}
} else if v, ok := d.GetOk("base_policy_regions"); ok && v.(*schema.Set).Len() > 0 {
regions = v.(*schema.Set).List()
}

policyDocumentTarget, err := buildCoreNetworkBasePolicyDocument(regions)

if err != nil {
return diag.Errorf("Formatting Core Network Base Policy: %s", err)
}

policyDocumentTarget := buildCoreNetworkBasePolicyDocument(region)
err := PutAndExecuteCoreNetworkPolicy(ctx, conn, d.Id(), policyDocumentTarget)
err = PutAndExecuteCoreNetworkPolicy(ctx, conn, d.Id(), policyDocumentTarget)

if err != nil {
return diag.FromErr(err)
Expand Down Expand Up @@ -609,6 +634,31 @@ func PutAndExecuteCoreNetworkPolicy(ctx context.Context, conn *networkmanager.Ne
}

// buildCoreNetworkBasePolicyDocument returns a base policy document
func buildCoreNetworkBasePolicyDocument(region string) string {
return fmt.Sprintf("{\"core-network-configuration\":{\"asn-ranges\":[\"64512-65534\"],\"edge-locations\":[{\"location\":\"%s\"}]},\"segments\":[{\"name\":\"segment\",\"description\":\"base-policy\"}],\"version\":\"2021.12\"}", region)
func buildCoreNetworkBasePolicyDocument(regions []interface{}) (string, error) {
edgeLocations := make([]*CoreNetworkEdgeLocation, len(regions))
for i, location := range regions {
edgeLocations[i] = &CoreNetworkEdgeLocation{Location: location.(string)}
}

basePolicy := &CoreNetworkPolicyDoc{
Version: "2021.12",
CoreNetworkConfiguration: &CoreNetworkPolicyCoreNetworkConfiguration{
AsnRanges: CoreNetworkPolicyDecodeConfigStringList([]interface{}{"64512-65534"}),
EdgeLocations: edgeLocations,
},
Segments: []*CoreNetworkPolicySegment{
{
Name: "segment",
Description: "base-policy",
},
},
}

b, err := json.MarshalIndent(basePolicy, "", " ")
if err != nil {
// should never happen if the above code is correct
return "", fmt.Errorf("building base policy document: %s", err)
}

return string(b), nil
}
171 changes: 171 additions & 0 deletions internal/service/networkmanager/core_network_policy_attachment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/aws/aws-sdk-go/service/networkmanager"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/hashicorp/terraform-provider-aws/internal/acctest"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
Expand Down Expand Up @@ -97,6 +98,42 @@ func TestAccNetworkManagerCoreNetworkPolicyAttachment_vpcAttachment(t *testing.T
})
}

func TestAccNetworkManagerCoreNetworkPolicyAttachment_vpcAttachmentMultiRegion(t *testing.T) {
ctx := acctest.Context(t)
var providers []*schema.Provider
resourceName := "aws_networkmanager_core_network_policy_attachment.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
acctest.PreCheck(t)
acctest.PreCheckMultipleRegion(t, 2)
},
ErrorCheck: acctest.ErrorCheck(t, networkmanager.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5FactoriesPlusProvidersAlternate(ctx, t, &providers),
CheckDestroy: testAccCheckCoreNetworkPolicyAttachmentDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccCoreNetworkPolicyAttachmentConfig_vpcAttachmentMultiRegionCreate(),
Check: resource.ComposeTestCheckFunc(
testAccCheckCoreNetworkPolicyAttachmentExists(ctx, resourceName),
resource.TestMatchResourceAttr(resourceName, "policy_document", regexp.MustCompile(fmt.Sprintf(`{"core-network-configuration":{"asn-ranges":\["65022-65534"\],"edge-locations":\[{"location":"%s"},{"location":"%s"}\],"vpn-ecmp-support":true},"segment-actions":\[{"action":"create-route","destination-cidr-blocks":\["10.0.0.0/16"\],"destinations":\["attachment-.+"\],"segment":"segment"},{"action":"create-route","destination-cidr-blocks":\["10.1.0.0/16"\],"destinations":\["attachment-.+"\],"segment":"segment2"}\],"segments":\[{"isolate-attachments":false,"name":"segment","require-attachment-acceptance":true},{"isolate-attachments":false,"name":"segment2","require-attachment-acceptance":true}\],"version":"2021.12"}`, acctest.Region(), acctest.AlternateRegion()))),
// use test below if the order of locations is unordered
// resource.TestCheckResourceAttr(resourceName, "policy_document", fmt.Sprintf("{\"core-network-configuration\":{\"asn-ranges\":[\"65022-65534\"],\"edge-locations\":[{\"location\":\"%s\"},{\"location\":\"%s\"}],\"vpn-ecmp-support\":true},\"segments\":[{\"description\":\"base-policy\",\"isolate-attachments\":false,\"name\":\"segment\",\"require-attachment-acceptance\":false}],\"version\":\"2021.12\"}", acctest.AlternateRegion(), acctest.Region())),
resource.TestCheckResourceAttrPair(resourceName, "core_network_id", "aws_networkmanager_core_network.test", "id"),
resource.TestCheckResourceAttrPair(resourceName, "id", "aws_networkmanager_core_network.test", "id"),
resource.TestCheckResourceAttr(resourceName, "state", networkmanager.CoreNetworkStateAvailable),
),
},
{
Config: testAccCoreNetworkPolicyAttachmentConfig_vpcAttachmentMultiRegionCreate(),
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccCheckCoreNetworkPolicyAttachmentDestroy(ctx context.Context) resource.TestCheckFunc {
// policy document will not be reverted to empty if the attachment is deleted
return nil
Expand Down Expand Up @@ -216,3 +253,137 @@ resource "aws_networkmanager_vpc_attachment" "test" {
}
`, acctest.Region()))
}

func testAccCoreNetworkPolicyAttachmentConfig_vpcAttachmentMultiRegionCreate() string {
return acctest.ConfigCompose(
acctest.ConfigMultipleRegionProvider(2),
acctest.ConfigAvailableAZsNoOptIn(),
fmt.Sprintf(`
resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "tf-acc-test-networkmanager-core-network-policy-attachment"
}
}
resource "aws_subnet" "test" {
count = 2
availability_zone = data.aws_availability_zones.available.names[count.index]
cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, count.index)
vpc_id = aws_vpc.test.id
tags = {
Name = "tf-acc-test-networkmanager-core-network-policy-attachment"
}
}
resource "aws_networkmanager_global_network" "test" {}
data "aws_networkmanager_core_network_policy_document" "test" {
core_network_configuration {
asn_ranges = ["65022-65534"]
edge_locations {
location = %[1]q
}
edge_locations {
location = %[2]q
}
}
segments {
name = "segment"
}
segments {
name = "segment2"
}
segment_actions {
action = "create-route"
segment = "segment"
destination_cidr_blocks = [
"10.0.0.0/16"
]
destinations = [
aws_networkmanager_vpc_attachment.test.id,
]
}
segment_actions {
action = "create-route"
segment = "segment2"
destination_cidr_blocks = [
"10.1.0.0/16"
]
destinations = [
aws_networkmanager_vpc_attachment.alternate_region.id,
]
}
}
resource "aws_networkmanager_core_network" "test" {
global_network_id = aws_networkmanager_global_network.test.id
base_policy_regions = [%[1]q, %[2]q]
create_base_policy = true
}
resource "aws_networkmanager_core_network_policy_attachment" "test" {
core_network_id = aws_networkmanager_core_network.test.id
policy_document = data.aws_networkmanager_core_network_policy_document.test.json
}
resource "aws_networkmanager_vpc_attachment" "test" {
core_network_id = aws_networkmanager_core_network.test.id
subnet_arns = aws_subnet.test[*].arn
vpc_arn = aws_vpc.test.arn
}
# Alternate region
data "aws_availability_zones" "alternate_region_available" {
provider = "awsalternate"
state = "available"
filter {
name = "opt-in-status"
values = ["opt-in-not-required"]
}
}
resource "aws_vpc" "alternate_region" {
provider = "awsalternate"
cidr_block = "10.1.0.0/16"
tags = {
Name = "tf-acc-test-networkmanager-core-network-policy-attachment"
}
}
resource "aws_subnet" "alternate_region" {
provider = "awsalternate"
count = 2
availability_zone = data.aws_availability_zones.alternate_region_available.names[count.index]
cidr_block = cidrsubnet(aws_vpc.alternate_region.cidr_block, 8, count.index)
vpc_id = aws_vpc.alternate_region.id
tags = {
Name = "tf-acc-test-networkmanager-core-network-policy-attachment"
}
}
resource "aws_networkmanager_vpc_attachment" "alternate_region" {
provider = "awsalternate"
core_network_id = aws_networkmanager_core_network.test.id
subnet_arns = aws_subnet.alternate_region[*].arn
vpc_arn = aws_vpc.alternate_region.arn
}
`, acctest.Region(), acctest.AlternateRegion()))
}
Loading

0 comments on commit 859c562

Please sign in to comment.