diff --git a/.changelog/29097.txt b/.changelog/29097.txt new file mode 100644 index 00000000000..eef11662ad1 --- /dev/null +++ b/.changelog/29097.txt @@ -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. +``` + diff --git a/internal/provider/provider.go b/internal/provider/provider.go index c9cbd1c137c..2b05dc91012 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -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(), diff --git a/internal/service/networkmanager/core_network.go b/internal/service/networkmanager/core_network.go index 0cd291243b0..ba27905ebaa 100644 --- a/internal/service/networkmanager/core_network.go +++ b/internal/service/networkmanager/core_network.go @@ -2,6 +2,7 @@ package networkmanager import ( "context" + "fmt" "log" "time" @@ -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, @@ -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, @@ -103,6 +118,7 @@ func ResourceCoreNetwork() *schema.Resource { json, _ := structure.NormalizeJsonString(v) return json }, + ConflictsWith: []string{"create_base_policy"}, }, "segments": { Type: schema.TypeList, @@ -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()) } @@ -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) @@ -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) + } } } @@ -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), } @@ -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) +} diff --git a/internal/service/networkmanager/core_network_policy_attachment.go b/internal/service/networkmanager/core_network_policy_attachment.go new file mode 100644 index 00000000000..854b4861b59 --- /dev/null +++ b/internal/service/networkmanager/core_network_policy_attachment.go @@ -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) +} diff --git a/internal/service/networkmanager/core_network_policy_attachment_test.go b/internal/service/networkmanager/core_network_policy_attachment_test.go new file mode 100644 index 00000000000..725c11e2b7e --- /dev/null +++ b/internal/service/networkmanager/core_network_policy_attachment_test.go @@ -0,0 +1,218 @@ +package networkmanager_test + +import ( + "context" + "fmt" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/service/networkmanager" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfnetworkmanager "github.com/hashicorp/terraform-provider-aws/internal/service/networkmanager" +) + +func TestAccNetworkManagerCoreNetworkPolicyAttachment_basic(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_networkmanager_core_network_policy_attachment.test" + + originalSegmentValue := "segmentValue1" + updatedSegmentValue := "segmentValue2" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, networkmanager.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckCoreNetworkPolicyAttachmentDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccCoreNetworkPolicyAttachmentConfig_basic(originalSegmentValue), + Check: resource.ComposeTestCheckFunc( + testAccCheckCoreNetworkPolicyAttachmentExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "policy_document", fmt.Sprintf("{\"core-network-configuration\":{\"asn-ranges\":[\"65022-65534\"],\"edge-locations\":[{\"location\":\"%s\"}],\"vpn-ecmp-support\":true},\"segments\":[{\"isolate-attachments\":false,\"name\":\"%s\",\"require-attachment-acceptance\":true}],\"version\":\"2021.12\"}", acctest.Region(), originalSegmentValue)), + 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), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccCoreNetworkPolicyAttachmentConfig_basic(updatedSegmentValue), + Check: resource.ComposeTestCheckFunc( + testAccCheckCoreNetworkPolicyAttachmentExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "policy_document", fmt.Sprintf("{\"core-network-configuration\":{\"asn-ranges\":[\"65022-65534\"],\"edge-locations\":[{\"location\":\"%s\"}],\"vpn-ecmp-support\":true},\"segments\":[{\"isolate-attachments\":false,\"name\":\"%s\",\"require-attachment-acceptance\":true}],\"version\":\"2021.12\"}", acctest.Region(), updatedSegmentValue)), + 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), + ), + }, + }, + }) +} + +func TestAccNetworkManagerCoreNetworkPolicyAttachment_vpcAttachment(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_networkmanager_core_network_policy_attachment.test" + + segmentValue := "segmentValue" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, networkmanager.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckCoreNetworkPolicyAttachmentDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccCoreNetworkPolicyAttachmentConfig_vpcAttachmentCreate(), + 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"}\],"vpn-ecmp-support":true},"segment-actions":\[{"action":"create-route","destination-cidr-blocks":\["0.0.0.0/0"\],"destinations":\["attachment-.+"\],"segment":"segment"}\],"segments":\[{"isolate-attachments":false,"name":"segment","require-attachment-acceptance":true}\],"version":"2021.12"}`, 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), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccCoreNetworkPolicyAttachmentConfig_basic(segmentValue), + Check: resource.ComposeTestCheckFunc( + testAccCheckCoreNetworkPolicyAttachmentExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "policy_document", fmt.Sprintf("{\"core-network-configuration\":{\"asn-ranges\":[\"65022-65534\"],\"edge-locations\":[{\"location\":\"%s\"}],\"vpn-ecmp-support\":true},\"segments\":[{\"isolate-attachments\":false,\"name\":\"%s\",\"require-attachment-acceptance\":true}],\"version\":\"2021.12\"}", acctest.Region(), segmentValue)), + 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), + ), + }, + }, + }) +} + +func testAccCheckCoreNetworkPolicyAttachmentDestroy(ctx context.Context) resource.TestCheckFunc { + // policy document will not be reverted to empty if the attachment is deleted + return nil +} + +func testAccCheckCoreNetworkPolicyAttachmentExists(ctx context.Context, n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Network Manager Core Network ID is set") + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).NetworkManagerConn() + + _, err := tfnetworkmanager.FindCoreNetworkPolicyByID(ctx, conn, rs.Primary.ID) + + return err + } +} + +func testAccCoreNetworkPolicyAttachmentConfig_basic(segmentValue string) string { + return fmt.Sprintf(` +resource "aws_networkmanager_global_network" "test" {} + +data "aws_networkmanager_core_network_policy_document" "test" { + core_network_configuration { + asn_ranges = ["65022-65534"] + + edge_locations { + location = %[2]q + } + } + + segments { + name = %[1]q + } +} + +resource "aws_networkmanager_core_network" "test" { + global_network_id = aws_networkmanager_global_network.test.id +} + +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 +} +`, segmentValue, acctest.Region()) +} + +func testAccCoreNetworkPolicyAttachmentConfig_vpcAttachmentCreate() string { + return acctest.ConfigCompose(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 + } + } + + segments { + name = "segment" + } + + segment_actions { + action = "create-route" + segment = "segment" + destination_cidr_blocks = [ + "0.0.0.0/0" + ] + destinations = [ + aws_networkmanager_vpc_attachment.test.id, + ] + } +} + +resource "aws_networkmanager_core_network" "test" { + global_network_id = aws_networkmanager_global_network.test.id + 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 +} +`, acctest.Region())) +} diff --git a/internal/service/networkmanager/core_network_test.go b/internal/service/networkmanager/core_network_test.go index 946c695effc..20e2f6a77e8 100644 --- a/internal/service/networkmanager/core_network_test.go +++ b/internal/service/networkmanager/core_network_test.go @@ -33,14 +33,16 @@ func TestAccNetworkManagerCoreNetwork_basic(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "created_at"), resource.TestCheckResourceAttr(resourceName, "description", ""), resource.TestMatchResourceAttr(resourceName, "id", regexp.MustCompile(`core-network-.+`)), + resource.TestCheckResourceAttr(resourceName, "policy_document", ""), resource.TestCheckResourceAttr(resourceName, "state", networkmanager.CoreNetworkStateAvailable), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"create_base_policy"}, }, }, }) @@ -87,9 +89,10 @@ func TestAccNetworkManagerCoreNetwork_tags(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"create_base_policy"}, }, { Config: testAccCoreNetworkConfig_tags2("key1", "value1updated", "key2", "value2"), @@ -132,9 +135,10 @@ func TestAccNetworkManagerCoreNetwork_description(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"create_base_policy"}, }, { Config: testAccCoreNetworkConfig_description(updatedDescription), @@ -149,7 +153,6 @@ func TestAccNetworkManagerCoreNetwork_description(t *testing.T) { func TestAccNetworkManagerCoreNetwork_policyDocument(t *testing.T) { ctx := acctest.Context(t) - client := acctest.Provider.Meta().(*conns.AWSClient) resourceName := "aws_networkmanager_core_network.test" originalSegmentValue := "segmentValue1" updatedSegmentValue := "segmentValue2" @@ -164,38 +167,39 @@ func TestAccNetworkManagerCoreNetwork_policyDocument(t *testing.T) { Config: testAccCoreNetworkConfig_policyDocument(originalSegmentValue), Check: resource.ComposeTestCheckFunc( testAccCheckCoreNetworkExists(ctx, resourceName), - testAccCheckPolicyDocument(resourceName, originalSegmentValue), + resource.TestCheckResourceAttr(resourceName, "policy_document", fmt.Sprintf("{\"core-network-configuration\":{\"asn-ranges\":[\"65022-65534\"],\"edge-locations\":[{\"location\":\"%s\"}],\"vpn-ecmp-support\":true},\"segments\":[{\"isolate-attachments\":false,\"name\":\"%s\",\"require-attachment-acceptance\":true}],\"version\":\"2021.12\"}", acctest.Region(), originalSegmentValue)), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "edges.*", map[string]string{ "asn": "65022", - "edge_location": client.Region, + "edge_location": acctest.Region(), "inside_cidr_blocks.#": "0", }), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "segments.*", map[string]string{ "edge_locations.#": "1", - "edge_locations.0": client.Region, + "edge_locations.0": acctest.Region(), "name": originalSegmentValue, "shared_segments.#": "0", }), ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"create_base_policy"}, }, { Config: testAccCoreNetworkConfig_policyDocument(updatedSegmentValue), Check: resource.ComposeTestCheckFunc( testAccCheckCoreNetworkExists(ctx, resourceName), - testAccCheckPolicyDocument(resourceName, updatedSegmentValue), + resource.TestCheckResourceAttr(resourceName, "policy_document", fmt.Sprintf("{\"core-network-configuration\":{\"asn-ranges\":[\"65022-65534\"],\"edge-locations\":[{\"location\":\"%s\"}],\"vpn-ecmp-support\":true},\"segments\":[{\"isolate-attachments\":false,\"name\":\"%s\",\"require-attachment-acceptance\":true}],\"version\":\"2021.12\"}", acctest.Region(), updatedSegmentValue)), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "edges.*", map[string]string{ "asn": "65022", - "edge_location": client.Region, + "edge_location": acctest.Region(), "inside_cidr_blocks.#": "0", }), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "segments.*", map[string]string{ "edge_locations.#": "1", - "edge_locations.0": client.Region, + "edge_locations.0": acctest.Region(), "name": updatedSegmentValue, "shared_segments.#": "0", }), @@ -205,22 +209,131 @@ func TestAccNetworkManagerCoreNetwork_policyDocument(t *testing.T) { }) } -func testAccCheckPolicyDocument(n, segmentValue string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found: %s", n) - } - client := acctest.Provider.Meta().(*conns.AWSClient) +func TestAccNetworkManagerCoreNetwork_createBasePolicyDocumentWithoutRegion(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_networkmanager_core_network.test" - policyDocumentTarget := fmt.Sprintf("{\"core-network-configuration\":{\"asn-ranges\":[\"65022-65534\"],\"edge-locations\":[{\"location\":\"%s\"}],\"vpn-ecmp-support\":true},\"segments\":[{\"isolate-attachments\":false,\"name\":\"%s\",\"require-attachment-acceptance\":true}],\"version\":\"2021.12\"}", client.Region, segmentValue) + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, networkmanager.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckCoreNetworkDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccCoreNetworkConfig_basePolicyDocumentWithoutRegion(), + Check: resource.ComposeTestCheckFunc( + testAccCheckCoreNetworkExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "create_base_policy", "true"), + resource.TestCheckResourceAttr(resourceName, "policy_document", fmt.Sprintf("{\"core-network-configuration\":{\"asn-ranges\":[\"64512-65534\"],\"edge-locations\":[{\"location\":\"%s\"}]},\"segments\":[{\"description\":\"base-policy\",\"name\":\"segment\"}],\"version\":\"2021.12\"}", acctest.Region())), + resource.TestCheckNoResourceAttr(resourceName, "base_policy_region"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "edges.*", map[string]string{ + "asn": "64512", + "edge_location": acctest.Region(), + "inside_cidr_blocks.#": "0", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "segments.*", map[string]string{ + "edge_locations.#": "1", + "edge_locations.0": acctest.Region(), + "name": "segment", + "shared_segments.#": "0", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"create_base_policy"}, + }, + }, + }) +} - if rs.Primary.Attributes["policy_document"] != policyDocumentTarget { - return fmt.Errorf("Expected policy_document: %s, given %s", policyDocumentTarget, rs.Primary.Attributes["policy_document"]) - } +func TestAccNetworkManagerCoreNetwork_createBasePolicyDocumentWithRegion(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_networkmanager_core_network.test" - return nil - } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, networkmanager.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckCoreNetworkDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccCoreNetworkConfig_basePolicyDocumentWithRegion(), + Check: resource.ComposeTestCheckFunc( + testAccCheckCoreNetworkExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "create_base_policy", "true"), + resource.TestCheckResourceAttr(resourceName, "policy_document", fmt.Sprintf("{\"core-network-configuration\":{\"asn-ranges\":[\"64512-65534\"],\"edge-locations\":[{\"location\":\"%s\"}]},\"segments\":[{\"description\":\"base-policy\",\"name\":\"segment\"}],\"version\":\"2021.12\"}", acctest.AlternateRegion())), + resource.TestCheckResourceAttr(resourceName, "base_policy_region", acctest.AlternateRegion()), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "edges.*", map[string]string{ + "asn": "64512", + "edge_location": acctest.AlternateRegion(), + "inside_cidr_blocks.#": "0", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "segments.*", map[string]string{ + "edge_locations.#": "1", + "edge_locations.0": acctest.AlternateRegion(), + "name": "segment", + "shared_segments.#": "0", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"base_policy_region", "create_base_policy"}, + }, + }, + }) +} + +func TestAccNetworkManagerCoreNetwork_withoutPolicyDocumentUpdateToCreateBasePolicyDocument(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_networkmanager_core_network.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, networkmanager.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckCoreNetworkDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccCoreNetworkConfig_basic(), + Check: resource.ComposeTestCheckFunc( + testAccCheckCoreNetworkExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "policy_document", ""), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"create_base_policy"}, + }, + { + Config: testAccCoreNetworkConfig_basePolicyDocumentWithoutRegion(), + Check: resource.ComposeTestCheckFunc( + testAccCheckCoreNetworkExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "create_base_policy", "true"), + resource.TestCheckResourceAttr(resourceName, "policy_document", fmt.Sprintf("{\"core-network-configuration\":{\"asn-ranges\":[\"64512-65534\"],\"edge-locations\":[{\"location\":\"%s\"}]},\"segments\":[{\"description\":\"base-policy\",\"name\":\"segment\"}],\"version\":\"2021.12\"}", acctest.Region())), + resource.TestCheckNoResourceAttr(resourceName, "base_policy_region"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "edges.*", map[string]string{ + "asn": "64512", + "edge_location": acctest.Region(), + "inside_cidr_blocks.#": "0", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "segments.*", map[string]string{ + "edge_locations.#": "1", + "edge_locations.0": acctest.Region(), + "name": "segment", + "shared_segments.#": "0", + }), + ), + }, + }, + }) } func testAccCheckCoreNetworkDestroy(ctx context.Context) resource.TestCheckFunc { @@ -279,8 +392,6 @@ resource "aws_networkmanager_core_network" "test" { func testAccCoreNetworkConfig_tags1(tagKey1, tagValue1 string) string { return fmt.Sprintf(` -data "aws_region" "current" {} - resource "aws_networkmanager_global_network" "test" {} resource "aws_networkmanager_core_network" "test" { @@ -295,8 +406,6 @@ resource "aws_networkmanager_core_network" "test" { func testAccCoreNetworkConfig_tags2(tagKey1, tagValue1, tagKey2, tagValue2 string) string { return fmt.Sprintf(` -data "aws_region" "current" {} - resource "aws_networkmanager_global_network" "test" {} resource "aws_networkmanager_core_network" "test" { @@ -312,8 +421,6 @@ resource "aws_networkmanager_core_network" "test" { func testAccCoreNetworkConfig_description(description string) string { return fmt.Sprintf(` -data "aws_region" "current" {} - resource "aws_networkmanager_global_network" "test" {} resource "aws_networkmanager_core_network" "test" { @@ -325,8 +432,6 @@ resource "aws_networkmanager_core_network" "test" { func testAccCoreNetworkConfig_policyDocument(segmentValue string) string { return fmt.Sprintf(` -data "aws_region" "current" {} - resource "aws_networkmanager_global_network" "test" {} data "aws_networkmanager_core_network_policy_document" "test" { @@ -334,7 +439,7 @@ data "aws_networkmanager_core_network_policy_document" "test" { asn_ranges = ["65022-65534"] edge_locations { - location = data.aws_region.current.name + location = %[2]q } } @@ -347,5 +452,28 @@ resource "aws_networkmanager_core_network" "test" { global_network_id = aws_networkmanager_global_network.test.id policy_document = data.aws_networkmanager_core_network_policy_document.test.json } -`, segmentValue) +`, segmentValue, acctest.Region()) +} + +func testAccCoreNetworkConfig_basePolicyDocumentWithoutRegion() string { + return ` +resource "aws_networkmanager_global_network" "test" {} + +resource "aws_networkmanager_core_network" "test" { + global_network_id = aws_networkmanager_global_network.test.id + create_base_policy = true +} +` +} + +func testAccCoreNetworkConfig_basePolicyDocumentWithRegion() string { + return fmt.Sprintf(` +resource "aws_networkmanager_global_network" "test" {} + +resource "aws_networkmanager_core_network" "test" { + global_network_id = aws_networkmanager_global_network.test.id + base_policy_region = %[1]q + create_base_policy = true +} +`, acctest.AlternateRegion()) } diff --git a/website/docs/r/networkmanager_core_network.html.markdown b/website/docs/r/networkmanager_core_network.html.markdown index b35efa71fa1..fd1fda0691f 100644 --- a/website/docs/r/networkmanager_core_network.html.markdown +++ b/website/docs/r/networkmanager_core_network.html.markdown @@ -10,6 +10,8 @@ description: |- Provides a core network resource. +~> **NOTE on Core Networks and Policy Attachments:** For a given core network, this resource's `policy_document` argument is incompatible with using the [`aws_networkmanager_core_network_policy_attachment` resource](/docs/providers/aws/r/networkmanager_core_network_policy_attachment.html). When using this resource's `policy_document` argument and the `aws_networkmanager_core_network_policy_attachment` resource, both will attempt to manage the core network's policy document and Terraform will show a permanent difference. + ## Example Usage ### Basic @@ -29,25 +31,65 @@ resource "aws_networkmanager_core_network" "example" { } ``` -### With policy document +### With tags ```terraform resource "aws_networkmanager_core_network" "example" { global_network_id = aws_networkmanager_global_network.example.id - policy_document = data.aws_networkmanager_core_network_policy_document.example.json + + tags = { + "hello" = "world" + } } ``` -### With tags +### With VPC Attachment + +The example below illustrates the scenario where your policy document has static routes pointing to VPC attachments and you want to attach your VPCs to the core network before applying the desired policy document. Set the `create_base_policy` argument to `true` if your core network does not currently have any `LIVE` policies (e.g. this is the first `terraform apply` with the core network resource), since a `LIVE` policy is required before VPCs can be attached to the core network. Otherwise, if your core network already has a `LIVE` policy, you may exclude the `create_base_policy` argument. ```terraform -resource "aws_networkmanager_core_network" "example" { - global_network_id = aws_networkmanager_global_network.example.id +resource "aws_networkmanager_global_network" "example" {} - tags = { - "hello" = "world" +data "aws_networkmanager_core_network_policy_document" "example" { + core_network_configuration { + asn_ranges = ["65022-65534"] + + edge_locations { + location = "us-west-2" + } + } + + segments { + name = "segment" + } + + segment_actions { + action = "create-route" + segment = "segment" + destination_cidr_blocks = [ + "0.0.0.0/0" + ] + destinations = [ + aws_networkmanager_vpc_attachment.example.id, + ] } } + +resource "aws_networkmanager_core_network" "example" { + global_network_id = aws_networkmanager_global_network.example.id + create_base_policy = true +} + +resource "aws_networkmanager_core_network_policy_attachment" "example" { + core_network_id = aws_networkmanager_core_network.example.id + policy_document = data.aws_networkmanager_core_network_policy_document.example.json +} + +resource "aws_networkmanager_vpc_attachment" "example" { + core_network_id = aws_networkmanager_core_network.example.id + subnet_arns = aws_subnet.example[*].arn + vpc_arn = aws_vpc.example.arn +} ``` ## Argument Reference @@ -55,8 +97,33 @@ resource "aws_networkmanager_core_network" "example" { The following arguments are supported: * `description` - (Optional) Description of the Core Network. +* `base_policy_region` - (Optional) The base policy created by setting the `create_base_policy` argument to `true` requires a region to be set in the `edge-locations`, `location` key. If `base_policy_region` is not specified, the region used in the base policy defaults to the region specified in the `provider` block. +* `create_base_policy` - (Optional) Specifies whether to create a base policy when a core network is created or updated. A base policy is created and set to `LIVE` to allow attachments to the core network (e.g. VPC Attachments) before applying a policy document provided using the [`aws_networkmanager_core_network_policy_attachment` resource](/docs/providers/aws/r/networkmanager_core_network_policy_attachment.html). This base policy is needed if your core network does not have any `LIVE` policies (e.g. a core network resource created without the `policy_document` argument) and your policy document has static routes pointing to VPC attachments and you want to attach your VPCs to the core network before applying the desired policy document. Valid values are `true` or `false`. Conflicts with `policy_document`. An example of this Terraform snippet can be found [above](#with-vpc-attachment). An example of a base policy created is shown below. The region specified in the `location` key can be configured using the `base_policy_region` argument. If `base_policy_region` is not specified, the region defaults to the region specified in the `provider` block. This base policy is overridden with the policy that you specify in the [`aws_networkmanager_core_network_policy_attachment` resource](/docs/providers/aws/r/networkmanager_core_network_policy_attachment.html). + +```json +{ + "version": "2021.12", + "core-network-configuration": { + "asn-ranges": [ + "64512-65534" + ], + "edge-locations": [ + { + "location": "us-east-1" + } + ] + }, + "segments": [ + { + "name": "segment", + "description": "base-policy" + } + ] +} +``` + * `global_network_id` - (Required) The ID of the global network that a core network will be a part of. -* `policy_document` - (Optional) Policy document for creating a core network. Note that updating this argument will result in the new policy document version being set as the `LATEST` and `LIVE` policy document. Refer to the [Core network policies documentation](https://docs.aws.amazon.com/network-manager/latest/cloudwan/cloudwan-policy-change-sets.html) for more information. +* `policy_document` - (Optional, **Deprecated** use the [`aws_networkmanager_core_network_policy_attachment`](networkmanager_core_network_policy_attachment.html) resource instead) Policy document for creating a core network. Note that updating this argument will result in the new policy document version being set as the `LATEST` and `LIVE` policy document. Refer to the [Core network policies documentation](https://docs.aws.amazon.com/network-manager/latest/cloudwan/cloudwan-policy-change-sets.html) for more information. Conflicts with `create_base_policy`. * `tags` - (Optional) Key-value tags for the Core Network. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. ## Timeouts diff --git a/website/docs/r/networkmanager_core_network_policy_attachment.html.markdown b/website/docs/r/networkmanager_core_network_policy_attachment.html.markdown new file mode 100644 index 00000000000..7a67700bd3d --- /dev/null +++ b/website/docs/r/networkmanager_core_network_policy_attachment.html.markdown @@ -0,0 +1,104 @@ +--- +subcategory: "Network Manager" +layout: "aws" +page_title: "AWS: aws_networkmanager_core_network_policy_attachment" +description: |- + Provides a Core Network Policy Attachment resource. +--- + +# Resource: aws_networkmanager_core_network_policy_attachment + +Provides a Core Network Policy Attachment resource. + +~> **NOTE on Core Networks and Policy Attachments:** For a given policy attachment, this resource is incompatible with using the [`aws_networkmanager_core_network` resource](/docs/providers/aws/r/networkmanager_core_network.html) `policy_document` argument. When using that argument and this resource, both will attempt to manage the core network's policy document and Terraform will show a permanent difference. + +## Example Usage + +### Basic + +```terraform +resource "aws_networkmanager_core_network" "example" { + global_network_id = aws_networkmanager_global_network.example.id +} + +resource "aws_networkmanager_core_network_policy_attachment" "example" { + core_network_id = aws_networkmanager_core_network.example.id + policy_document = data.aws_networkmanager_core_network_policy_document.example.json +} +``` + +### With VPC Attachment + +The example below illustrates the scenario where your policy document has static routes pointing to VPC attachments and you want to attach your VPCs to the core network before applying the desired policy document. Set the `create_base_policy` argument of the [`aws_networkmanager_core_network` resource](/docs/providers/aws/r/networkmanager_core_network.html) to `true` if your core network does not currently have any `LIVE` policies (e.g. this is the first `terraform apply` with the core network resource), since a `LIVE` policy is required before VPCs can be attached to the core network. Otherwise, if your core network already has a `LIVE` policy, you may exclude the `create_base_policy` argument. + +```terraform +resource "aws_networkmanager_global_network" "example" {} + +data "aws_networkmanager_core_network_policy_document" "example" { + core_network_configuration { + asn_ranges = ["65022-65534"] + + edge_locations { + location = "us-west-2" + } + } + + segments { + name = "segment" + } + + segment_actions { + action = "create-route" + segment = "segment" + destination_cidr_blocks = [ + "0.0.0.0/0" + ] + destinations = [ + aws_networkmanager_vpc_attachment.example.id, + ] + } +} + +resource "aws_networkmanager_core_network" "example" { + global_network_id = aws_networkmanager_global_network.example.id + create_base_policy = true +} + +resource "aws_networkmanager_core_network_policy_attachment" "example" { + core_network_id = aws_networkmanager_core_network.example.id + policy_document = data.aws_networkmanager_core_network_policy_document.example.json +} + +resource "aws_networkmanager_vpc_attachment" "example" { + core_network_id = aws_networkmanager_core_network.example.id + subnet_arns = aws_subnet.example[*].arn + vpc_arn = aws_vpc.example.arn +} +``` + +## Argument Reference + +The following arguments are supported: + +* `core_network_id` - (Required) The ID of the core network that a policy will be attached to and made `LIVE`. +* `policy_document` - (Required) Policy document for creating a core network. Note that updating this argument will result in the new policy document version being set as the `LATEST` and `LIVE` policy document. Refer to the [Core network policies documentation](https://docs.aws.amazon.com/network-manager/latest/cloudwan/cloudwan-policy-change-sets.html) for more information. + +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +* `update` - (Default `30m`). If this is the first time attaching a policy to a core network then this timeout value is also used as the `create` timeout value. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `state` - Current state of a core network. + +## Import + +`aws_networkmanager_core_network_policy_attachment` can be imported using the core network ID, e.g. + +``` +$ terraform import aws_networkmanager_core_network_policy_attachment.example core-network-0d47f6t230mz46dy4 +```