Skip to content

Commit

Permalink
Merge pull request #29097 from GlennChia/f-aws_networkmanager_core_ne…
Browse files Browse the repository at this point in the history
…twork_policy_attachment

r/aws_networkmanager_core_network_policy_attachment
  • Loading branch information
jar-b authored Feb 7, 2023
2 parents 35d19f8 + 52c339a commit da7030f
Show file tree
Hide file tree
Showing 8 changed files with 805 additions and 84 deletions.
12 changes: 12 additions & 0 deletions .changelog/29097.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
```release-note:new-resource
aws_networkmanager_core_network_policy_attachment
```

```release-note:enhancement
resource/aws_networkmanager_core_network: Add `base_policy_region` and `create_base_policy` arguments
```

```release-note:note
resource/aws_networkmanager_core_network: The `policy_document` attribute is being deprecated in favor of the new `aws_networkmanager_core_network_policy_attachment` resource.
```

1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -1821,6 +1821,7 @@ func New(ctx context.Context) (*schema.Provider, error) {
"aws_networkmanager_connect_attachment": networkmanager.ResourceConnectAttachment(),
"aws_networkmanager_connection": networkmanager.ResourceConnection(),
"aws_networkmanager_core_network": networkmanager.ResourceCoreNetwork(),
"aws_networkmanager_core_network_policy_attachment": networkmanager.ResourceCoreNetworkPolicyAttachment(),
"aws_networkmanager_customer_gateway_association": networkmanager.ResourceCustomerGatewayAssociation(),
"aws_networkmanager_device": networkmanager.ResourceDevice(),
"aws_networkmanager_global_network": networkmanager.ResourceGlobalNetwork(),
Expand Down
135 changes: 101 additions & 34 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"
"fmt"
"log"
"time"

Expand Down Expand Up @@ -55,6 +56,17 @@ func ResourceCoreNetwork() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"base_policy_region": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: verify.ValidRegionName,
},
"create_base_policy": {
Type: schema.TypeBool,
Optional: true,
Default: false,
ConflictsWith: []string{"policy_document"},
},
"created_at": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -92,8 +104,11 @@ func ResourceCoreNetwork() *schema.Resource {
ValidateFunc: validation.StringLenBetween(0, 50),
},
"policy_document": {
Deprecated: "Use the aws_networkmanager_core_network_policy_attachment resource instead. " +
"This attribute will be removed in the next major version of the provider.",
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.All(
validation.StringLenBetween(0, 10000000),
validation.StringIsJSON,
Expand All @@ -103,6 +118,7 @@ func ResourceCoreNetwork() *schema.Resource {
json, _ := structure.NormalizeJsonString(v)
return json
},
ConflictsWith: []string{"create_base_policy"},
},
"segments": {
Type: schema.TypeList,
Expand Down Expand Up @@ -156,6 +172,21 @@ func resourceCoreNetworkCreate(ctx context.Context, d *schema.ResourceData, meta
input.PolicyDocument = aws.String(v.(string))
}

// check if the user wants to create a base policy document
// this creates the core network with a starting policy document set to LIVE
// 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 v, ok := d.GetOk("base_policy_region"); ok {
region = v.(string)
}

policyDocumentTarget := buildCoreNetworkBasePolicyDocument(region)
input.PolicyDocument = aws.String(policyDocumentTarget)
}

if len(tags) > 0 {
input.Tags = Tags(tags.IgnoreAWS())
}
Expand Down Expand Up @@ -210,7 +241,7 @@ func resourceCoreNetworkRead(ctx context.Context, d *schema.ResourceData, meta i

// getting the policy document uses a different API call
// policy document is also optional
coreNetworkPolicy, err := findCoreNetworkPolicyByID(ctx, conn, d.Id())
coreNetworkPolicy, err := FindCoreNetworkPolicyByID(ctx, conn, d.Id())

if tfresource.NotFound(err) {
d.Set("policy_document", nil)
Expand Down Expand Up @@ -259,47 +290,35 @@ func resourceCoreNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta
}

if d.HasChange("policy_document") {
v, err := protocol.DecodeJSONValue(d.Get("policy_document").(string), protocol.NoEscape)
err := PutAndExecuteCoreNetworkPolicy(ctx, conn, d.Id(), d.Get("policy_document").(string))

if err != nil {
return diag.Errorf("decoding Network Manager Core Network (%s) policy document: %s", d.Id(), err)
return diag.FromErr(err)
}

output, err := conn.PutCoreNetworkPolicyWithContext(ctx, &networkmanager.PutCoreNetworkPolicyInput{
ClientToken: aws.String(resource.UniqueId()),
CoreNetworkId: aws.String(d.Id()),
PolicyDocument: v,
})

if err != nil {
return diag.Errorf("putting Network Manager Core Network (%s) policy: %s", d.Id(), err)
if _, err := waitCoreNetworkUpdated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil {
return diag.Errorf("waiting for Network Manager Core Network (%s) update: %s", d.Id(), err)
}
}

policyVersionID := aws.Int64Value(output.CoreNetworkPolicy.PolicyVersionId)
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 v, ok := d.GetOk("base_policy_region"); ok {
region = v.(string)
}

// new policy documents goes from Pending generation to Ready to execute
_, err = tfresource.RetryWhen(ctx, 4*time.Minute,
func() (interface{}, error) {
return conn.ExecuteCoreNetworkChangeSetWithContext(ctx, &networkmanager.ExecuteCoreNetworkChangeSetInput{
CoreNetworkId: aws.String(d.Id()),
PolicyVersionId: aws.Int64(policyVersionID),
})
},
func(err error) (bool, error) {
if tfawserr.ErrMessageContains(err, networkmanager.ErrCodeValidationException, "Incorrect input") {
return true, err
}
policyDocumentTarget := buildCoreNetworkBasePolicyDocument(region)
err := PutAndExecuteCoreNetworkPolicy(ctx, conn, d.Id(), policyDocumentTarget)

return false, err
},
)
if err != nil {
return diag.FromErr(err)
}

if err != nil {
return diag.Errorf("executing Network Manager Core Network (%s) change set (%d): %s", d.Id(), policyVersionID, err)
}

if _, err := waitCoreNetworkUpdated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil {
return diag.Errorf("waiting for Network Manager Core Network (%s) update: %s", d.Id(), err)
if _, err := waitCoreNetworkUpdated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil {
return diag.Errorf("waiting for Network Manager Core Network (%s) update: %s", d.Id(), err)
}
}
}

Expand Down Expand Up @@ -373,7 +392,7 @@ func FindCoreNetworkByID(ctx context.Context, conn *networkmanager.NetworkManage
return output.CoreNetwork, nil
}

func findCoreNetworkPolicyByID(ctx context.Context, conn *networkmanager.NetworkManager, id string) (*networkmanager.CoreNetworkPolicy, error) {
func FindCoreNetworkPolicyByID(ctx context.Context, conn *networkmanager.NetworkManager, id string) (*networkmanager.CoreNetworkPolicy, error) {
input := &networkmanager.GetCoreNetworkPolicyInput{
CoreNetworkId: aws.String(id),
}
Expand Down Expand Up @@ -544,3 +563,51 @@ func flattenCoreNetworkSegments(apiObjects []*networkmanager.CoreNetworkSegment)

return tfList
}

func PutAndExecuteCoreNetworkPolicy(ctx context.Context, conn *networkmanager.NetworkManager, coreNetworkId, policyDocument string) error {
v, err := protocol.DecodeJSONValue(policyDocument, protocol.NoEscape)

if err != nil {
return fmt.Errorf("decoding Network Manager Core Network (%s) policy document: %s", coreNetworkId, err)
}

output, err := conn.PutCoreNetworkPolicyWithContext(ctx, &networkmanager.PutCoreNetworkPolicyInput{
ClientToken: aws.String(resource.UniqueId()),
CoreNetworkId: aws.String(coreNetworkId),
PolicyDocument: v,
})

if err != nil {
return fmt.Errorf("putting Network Manager Core Network (%s) policy: %s", coreNetworkId, err)
}

policyVersionID := aws.Int64Value(output.CoreNetworkPolicy.PolicyVersionId)

// new policy documents goes from Pending generation to Ready to execute
_, err = tfresource.RetryWhen(ctx, 4*time.Minute,
func() (interface{}, error) {
return conn.ExecuteCoreNetworkChangeSetWithContext(ctx, &networkmanager.ExecuteCoreNetworkChangeSetInput{
CoreNetworkId: aws.String(coreNetworkId),
PolicyVersionId: aws.Int64(policyVersionID),
})
},
func(err error) (bool, error) {
if tfawserr.ErrMessageContains(err, networkmanager.ErrCodeValidationException, "Incorrect input") {
return true, err
}

return false, err
},
)

if err != nil {
return fmt.Errorf("executing Network Manager Core Network (%s) change set (%d): %s", coreNetworkId, policyVersionID, err)
}

return nil
}

// 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)
}
124 changes: 124 additions & 0 deletions internal/service/networkmanager/core_network_policy_attachment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package networkmanager

import (
"context"
"log"
"regexp"
"time"

"github.com/aws/aws-sdk-go/private/protocol"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
)

func ResourceCoreNetworkPolicyAttachment() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceCoreNetworkPolicyAttachmentCreate,
ReadWithoutTimeout: resourceCoreNetworkPolicyAttachmentRead,
UpdateWithoutTimeout: resourceCoreNetworkPolicyAttachmentUpdate,
DeleteWithoutTimeout: schema.NoopContext,

Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Timeouts: &schema.ResourceTimeout{
Update: schema.DefaultTimeout(30 * time.Minute),
},

Schema: map[string]*schema.Schema{
"core_network_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.All(
validation.StringLenBetween(0, 50),
validation.StringMatch(regexp.MustCompile(`^core-network-([0-9a-f]{8,17})$`), "must be a valid Core Network ID"),
),
},
"policy_document": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.All(
validation.StringLenBetween(0, 10000000),
validation.StringIsJSON,
),
DiffSuppressFunc: verify.SuppressEquivalentJSONDiffs,
StateFunc: func(v interface{}) string {
json, _ := structure.NormalizeJsonString(v)
return json
},
},
"state": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

func resourceCoreNetworkPolicyAttachmentCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
d.SetId(d.Get("core_network_id").(string))

return resourceCoreNetworkPolicyAttachmentUpdate(ctx, d, meta)
}

func resourceCoreNetworkPolicyAttachmentRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*conns.AWSClient).NetworkManagerConn()

coreNetwork, err := FindCoreNetworkByID(ctx, conn, d.Id())

if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] Network Manager Core Network %s not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return diag.Errorf("reading Network Manager Core Network (%s): %s", d.Id(), err)
}

d.Set("core_network_id", coreNetwork.CoreNetworkId)
d.Set("state", coreNetwork.State)

// getting the policy document uses a different API call
coreNetworkPolicy, err := FindCoreNetworkPolicyByID(ctx, conn, d.Id())

if tfresource.NotFound(err) {
d.Set("policy_document", nil)
} else if err != nil {
return diag.Errorf("reading Network Manager Core Network (%s) policy: %s", d.Id(), err)
} else {
encodedPolicyDocument, err := protocol.EncodeJSONValue(coreNetworkPolicy.PolicyDocument, protocol.NoEscape)

if err != nil {
return diag.Errorf("encoding Network Manager Core Network (%s) policy document: %s", d.Id(), err)
}

d.Set("policy_document", encodedPolicyDocument)
}
return nil
}

func resourceCoreNetworkPolicyAttachmentUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*conns.AWSClient).NetworkManagerConn()

if d.HasChange("policy_document") {
err := PutAndExecuteCoreNetworkPolicy(ctx, conn, d.Id(), d.Get("policy_document").(string))

if err != nil {
return diag.FromErr(err)
}

if _, err := waitCoreNetworkUpdated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil {
return diag.Errorf("waiting for Network Manager Core Network (%s) update: %s", d.Id(), err)
}
}

return resourceCoreNetworkPolicyAttachmentRead(ctx, d, meta)
}
Loading

0 comments on commit da7030f

Please sign in to comment.