From 8d710077d20780180fcc092b8431962e0fa0ab27 Mon Sep 17 00:00:00 2001 From: Dave DeRicco Date: Tue, 21 Jun 2022 12:14:07 -0400 Subject: [PATCH 01/30] First push to add MACSec support --- internal/service/directconnect/connection.go | 20 +++ internal/service/directconnect/macsec_key.go | 129 +++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 internal/service/directconnect/macsec_key.go diff --git a/internal/service/directconnect/connection.go b/internal/service/directconnect/connection.go index 55ac39a1dcc..ef3a4414640 100644 --- a/internal/service/directconnect/connection.go +++ b/internal/service/directconnect/connection.go @@ -53,6 +53,17 @@ func ResourceConnection() *schema.Resource { Required: true, ForceNew: true, }, + // Indicates whether the connection supports MAC Security (MACsec). + "macsec_capable": { + Type: schema.TypeBool, + Computed: true, + }, + // The MAC Security (MACsec) security keys associated with the connection. + "macsec_keys": { + // Slice of type MacSecKey + Type: schema.TypeList, + Optional: true, + }, "name": { Type: schema.TypeString, Required: true, @@ -62,6 +73,11 @@ func ResourceConnection() *schema.Resource { Type: schema.TypeString, Computed: true, }, + // The MAC Security (MACsec) port link status of the connection. + "port_encryption_status": { + Type: schema.TypeString, + Computed: true, + }, "provider_name": { Type: schema.TypeString, Optional: true, @@ -86,6 +102,7 @@ func resourceConnectionCreate(d *schema.ResourceData, meta interface{}) error { Bandwidth: aws.String(d.Get("bandwidth").(string)), ConnectionName: aws.String(name), Location: aws.String(d.Get("location").(string)), + RequestMACSec: aws.Bool(d.Get("macsec_enabled").(bool)), } if v, ok := d.GetOk("provider_name"); ok { @@ -138,8 +155,11 @@ func resourceConnectionRead(d *schema.ResourceData, meta interface{}) error { d.Set("has_logical_redundancy", connection.HasLogicalRedundancy) d.Set("jumbo_frame_capable", connection.JumboFrameCapable) d.Set("location", connection.Location) + d.Set("macsec_capable", connection.MacSecCapable) + d.Set("macsec_keys", connection.MacSecKeys) d.Set("name", connection.ConnectionName) d.Set("owner_account_id", connection.OwnerAccount) + d.Set("port_encryption_status", connection.PortEncryptionStatus) d.Set("provider_name", connection.ProviderName) tags, err := ListTags(conn, arn) diff --git a/internal/service/directconnect/macsec_key.go b/internal/service/directconnect/macsec_key.go new file mode 100644 index 00000000000..c0000bdb441 --- /dev/null +++ b/internal/service/directconnect/macsec_key.go @@ -0,0 +1,129 @@ +package directconnect + +import ( + "fmt" + "log" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-aws/internal/conns" +) + +func ResourceMacSecKey() *schema.Resource { + return &schema.Resource{ + // MacSecKey resource only supports create (Associate) and delete (Disassociate) + Create: resourceMacSecKeyCreate, + // Read is performed via DescribeConnection + Read: schema.Noop, + // No update functionality + Update: schema.Noop, + Delete: resourceMacSecKeyDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "cak": { + Type: schema.TypeString, + Optional: true, + // CAK requires CKN + RequiredWith: []string{"ckn"}, + }, + "ckn": { + Type: schema.TypeString, + Computed: true, + Optional: true, + AtLeastOneOf: []string{"ckn", "secret_arn"}, + }, + "connection_id": { + Type: schema.TypeString, + Required: true, + }, + "secret_arn": { + Type: schema.TypeString, + Optional: true, + Computed: true, + AtLeastOneOf: []string{"ckn", "secret_arn"}, + }, + "start_on": { + Type: schema.TypeString, + Computed: true, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceMacSecKeyCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).DirectConnectConn + + input := &directconnect.AssociateMacSecKeyInput{ + Cak: aws.String(d.Get("cak").(string)), + Ckn: aws.String(d.Get("ckn").(string)), + ConnectionId: aws.String(d.Get("connection_id").(string)), + SecretARN: aws.String(d.Get("secret_arn").(string)), + } + + log.Printf("[DEBUG] Creating MACSec secret key on Direct Connect Connection: %s", *input.ConnectionId) + output, err := conn.AssociateMacSecKey(input) + + if err != nil { + return fmt.Errorf("error creating MACSec secret key on Direct Connect Connection (%s): %w", *input.ConnectionId, err) + } + + secret_arn := MacSecKeyParseSecretArn(output) + + // Create a composite ID based on connection ID and secret ARN + d.SetId(fmt.Sprintf("%s:%s", secret_arn, aws.StringValue(output.ConnectionId))) + + return nil +} + +func resourceMacSecKeyDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).DirectConnectConn + + input := &directconnect.DisassociateMacSecKeyInput{ + ConnectionId: aws.String(d.Get("connection_id").(string)), + SecretARN: aws.String(d.Get("secret_arn").(string)), + } + + log.Printf("[DEBUG] Deleting MACSec secret key on Direct Connect Connection: %s", *input.ConnectionId) + _, err := conn.DisassociateMacSecKey(input) + + if err != nil { + return fmt.Errorf("error delecting MACSec secret key on Direct Connect Connection (%s): %w", *input.ConnectionId, err) + } + + return nil +} + +// MacSecKeyParseSecretArn parses the secret ARN returned from a CMK or secret_arn +func MacSecKeyParseSecretArn(output *directconnect.AssociateMacSecKeyOutput) string { + var result string + + for _, key := range output.MacSecKeys { + if key == nil { + continue + } + if key != nil { + result = *key.SecretARN + } + } + return result +} + +// Define an exported function to parse the resource ID +func MacSecKeyParseID(id string) (string, string, error) { + parts := strings.SplitN(id, ":", 2) + + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return "", "", fmt.Errorf("unexpected format of ID (%s), expected :connectionId", id) + } + + return parts[0], parts[1], nil +} From 5c4d093bc69f8e7030ce04e3977a1900fc5bfd11 Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Wed, 10 Aug 2022 17:10:30 -0400 Subject: [PATCH 02/30] Add working acceptance tests --- internal/provider/provider.go | 1 + internal/service/directconnect/connection.go | 158 +++++++++++++++++- .../service/directconnect/connection_test.go | 62 +++++++ internal/service/directconnect/macsec_key.go | 37 ++-- .../service/directconnect/macsec_key_test.go | 149 +++++++++++++++++ 5 files changed, 393 insertions(+), 14 deletions(-) create mode 100644 internal/service/directconnect/macsec_key_test.go diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 5bf75c2a60f..3b4d0a61a2a 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1265,6 +1265,7 @@ func New(_ context.Context) (*schema.Provider, error) { "aws_dx_hosted_transit_virtual_interface": directconnect.ResourceHostedTransitVirtualInterface(), "aws_dx_hosted_transit_virtual_interface_accepter": directconnect.ResourceHostedTransitVirtualInterfaceAccepter(), "aws_dx_lag": directconnect.ResourceLag(), + "aws_dx_macsec_key": directconnect.ResourceMacSecKey(), "aws_dx_private_virtual_interface": directconnect.ResourcePrivateVirtualInterface(), "aws_dx_public_virtual_interface": directconnect.ResourcePublicVirtualInterface(), "aws_dx_transit_virtual_interface": directconnect.ResourceTransitVirtualInterface(), diff --git a/internal/service/directconnect/connection.go b/internal/service/directconnect/connection.go index ef3a4414640..38d5f18f975 100644 --- a/internal/service/directconnect/connection.go +++ b/internal/service/directconnect/connection.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/service/directconnect" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" @@ -40,6 +41,13 @@ func ResourceConnection() *schema.Resource { ForceNew: true, ValidateFunc: validConnectionBandWidth(), }, + // The MAC Security (MACsec) connection encryption mode. + "encryption_mode": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"no_encrypt", "should_encrypt", "must_encrypt"}, false), + }, "has_logical_redundancy": { Type: schema.TypeString, Computed: true, @@ -58,11 +66,39 @@ func ResourceConnection() *schema.Resource { Type: schema.TypeBool, Computed: true, }, + // Enable or disable MAC Security (MACsec) on this connection. + "macsec_requested": { + Type: schema.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + }, // The MAC Security (MACsec) security keys associated with the connection. "macsec_keys": { // Slice of type MacSecKey Type: schema.TypeList, + Computed: true, Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "secret_arn": { + Type: schema.TypeString, + Computed: true, + }, + "ckn": { + Type: schema.TypeString, + Computed: true, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + "start_on": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, }, "name": { Type: schema.TypeString, @@ -102,7 +138,7 @@ func resourceConnectionCreate(d *schema.ResourceData, meta interface{}) error { Bandwidth: aws.String(d.Get("bandwidth").(string)), ConnectionName: aws.String(name), Location: aws.String(d.Get("location").(string)), - RequestMACSec: aws.Bool(d.Get("macsec_enabled").(bool)), + RequestMACSec: aws.Bool(d.Get("macsec_requested").(bool)), } if v, ok := d.GetOk("provider_name"); ok { @@ -121,6 +157,7 @@ func resourceConnectionCreate(d *schema.ResourceData, meta interface{}) error { } d.SetId(aws.StringValue(output.ConnectionId)) + d.Set("macsec_requested", d.Get("macsec_requested").(bool)) return resourceConnectionRead(d, meta) } @@ -152,16 +189,38 @@ func resourceConnectionRead(d *schema.ResourceData, meta interface{}) error { d.Set("arn", arn) d.Set("aws_device", connection.AwsDeviceV2) d.Set("bandwidth", connection.Bandwidth) + d.Set("encryption_mode", connection.EncryptionMode) d.Set("has_logical_redundancy", connection.HasLogicalRedundancy) d.Set("jumbo_frame_capable", connection.JumboFrameCapable) d.Set("location", connection.Location) d.Set("macsec_capable", connection.MacSecCapable) - d.Set("macsec_keys", connection.MacSecKeys) + // d.Set("macsec_keys", connection.MacSecKeys) d.Set("name", connection.ConnectionName) d.Set("owner_account_id", connection.OwnerAccount) d.Set("port_encryption_status", connection.PortEncryptionStatus) d.Set("provider_name", connection.ProviderName) + // if connection.MacSecKeys != nil { + // keys := make([]interface{}, len(connection.MacSecKeys)) + // for i, key := range connection.MacSecKeys { + // k := map[string]interface{}{ + // "Ckn": aws.StringValue(key.Ckn), + // "SecretARN": aws.StringValue(key.SecretARN), + // "StartOn": aws.StringValue(key.StartOn), + // "State": aws.StringValue(key.State), + // } + // fmt.Println(k) + // keys[i] = k + // } + // d.Set("macsec_keys", keys) + // } + + // fmt.Println("MACSec keys:", connection.MacSecKeys) + + // if err := d.Set("macsec_keys", flattenMacSecKeys(connection.MacSecKeys)); err != nil { + // return fmt.Errorf("error setting macsec_keys: %s", err) + // } + tags, err := ListTags(conn, arn) if err != nil { @@ -185,6 +244,19 @@ func resourceConnectionRead(d *schema.ResourceData, meta interface{}) error { func resourceConnectionUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).DirectConnectConn + // Update encryption mode + if d.HasChange("encryption_mode") { + input := &directconnect.UpdateConnectionInput{ + ConnectionId: aws.String(d.Id()), + EncryptionMode: aws.String(d.Get("encryption_mode").(string)), + } + log.Printf("[DEBUG] Modifying Direct Connect connection attributes: %s", input) + _, err := conn.UpdateConnection(input) + if err != nil { + return fmt.Errorf("error modifying Direct Connect connection (%s) attributes: %s", d.Id(), err) + } + } + arn := d.Get("arn").(string) if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") @@ -225,3 +297,85 @@ func deleteConnection(conn *directconnect.DirectConnect, connectionID string, wa return nil } + +// ValidateMacSecAvailability checks for MAC Security availability given a +// location `l` and desired port speed `p` +func ValidateMacSecAvailability(l string, p string, meta interface{}) bool { + conn := meta.(*conns.AWSClient).DirectConnectConn + input := &directconnect.DescribeLocationsInput{} + + response, err := conn.DescribeLocations(input) + + if err != nil { + fmt.Printf("error checking MACSec availability: %s", err) + return false + } + + var available bool + + for _, location := range response.Locations { + if l == *location.LocationCode { + for _, port_speed := range location.AvailableMacSecPortSpeeds { + if *port_speed == p { + available = true + } else { + fmt.Printf("MACSec not available for location %s and desired port speed %s", l, p) + available = false + } + } + } + } + return available +} + +// Expand and flatten structures for MACSec keys +// Ref: https://github.com/hashicorp/terraform-provider-aws/blob/main/docs/contributing/data-handling-and-conversion.md#flatten-functions-for-blocks + +func flattenMacSecKey(macSecKey *directconnect.MacSecKey) map[string]interface{} { + if macSecKey == nil { + return nil + } + + keyMap := map[string]interface{}{} + + // nested attribute handling + + return keyMap +} + +func flattenMacSecKeyStructures(macSecKeys []*directconnect.MacSecKey) []interface{} { + if len(macSecKeys) == 0 { + return nil + } + + var keyList []interface{} + + for _, key := range macSecKeys { + if key == nil { + continue + } + + keyList = append(keyList, flattenMacSecKey(key)) + } + return keyList + + // if macSecKeys == nil { + // return []interface{}{} + // } + + // // fmt.Println("Length of MACSeckeys:", len(macSecKeys)) + + // keys := make([]interface{}, len(macSecKeys)) + // for i, key := range macSecKeys { + // k := map[string]interface{}{ + // "Ckn": aws.StringValue(key.Ckn), + // "SecretARN": aws.StringValue(key.SecretARN), + // "StartOn": aws.StringValue(key.StartOn), + // "State": aws.StringValue(key.State), + // } + // fmt.Println(k) + // keys[i] = k + // } + + // return []interface{}{keys} +} diff --git a/internal/service/directconnect/connection_test.go b/internal/service/directconnect/connection_test.go index f9db8399cdd..98008f34659 100644 --- a/internal/service/directconnect/connection_test.go +++ b/internal/service/directconnect/connection_test.go @@ -72,6 +72,44 @@ func TestAccDirectConnectConnection_disappears(t *testing.T) { }) } +func TestAccDirectConnectConnection_macsecRequested(t *testing.T) { + var connection directconnect.Connection + resourceName := "aws_dx_connection.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckConnectionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConnectionConfig_macsecEnabled(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConnectionExists(resourceName, &connection), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(`dxcon/.+`)), + resource.TestCheckResourceAttr(resourceName, "bandwidth", "100Gbps"), + resource.TestCheckResourceAttrSet(resourceName, "location"), + // macsec_capable will not return "true" while connection is in "Requesting" state + resource.TestCheckResourceAttr(resourceName, "macsec_capable", "false"), + resource.TestCheckResourceAttr(resourceName, "macsec_requested", "true"), + acctest.CheckResourceAttrAccountID(resourceName, "owner_account_id"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttrSet(resourceName, "provider_name"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + // Ignore the "macsec_requested" attribute as isn't returned by the API during read/refresh + ImportStateVerifyIgnore: []string{"macsec_requested"}, + }, + }, + }) +} + func TestAccDirectConnectConnection_providerName(t *testing.T) { var connection directconnect.Connection resourceName := "aws_dx_connection.test" @@ -221,6 +259,30 @@ resource "aws_dx_connection" "test" { `, rName) } +func testAccConnectionConfig_macsecEnabled(rName string) string { + return fmt.Sprintf(` +data "aws_dx_locations" "test" {} + +locals { + location_codes = tolist(data.aws_dx_locations.test.location_codes) + idx = min(2, length(local.location_codes) - 1) +} + +data "aws_dx_location" "test" { + location_code = local.location_codes[local.idx] +} + +resource "aws_dx_connection" "test" { + name = %[1]q + bandwidth = "100Gbps" + location = data.aws_dx_location.test.location_code + macsec_requested = true + + provider_name = data.aws_dx_location.test.available_providers[0] +} +`, rName) +} + func testAccConnectionConfig_providerName(rName string) string { return fmt.Sprintf(` data "aws_dx_locations" "test" {} diff --git a/internal/service/directconnect/macsec_key.go b/internal/service/directconnect/macsec_key.go index c0000bdb441..664363736bf 100644 --- a/internal/service/directconnect/macsec_key.go +++ b/internal/service/directconnect/macsec_key.go @@ -3,11 +3,13 @@ package directconnect import ( "fmt" "log" + "regexp" "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directconnect" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" ) @@ -15,9 +17,10 @@ func ResourceMacSecKey() *schema.Resource { return &schema.Resource{ // MacSecKey resource only supports create (Associate) and delete (Disassociate) Create: resourceMacSecKeyCreate, - // Read is performed via DescribeConnection - Read: schema.Noop, - // No update functionality + Read: schema.Noop, + // You cannot modify a MACsec secret key after you associate it with a connection. + // To modify the key, disassociate the key from the connection, and then associate + // a new key with the connection Update: schema.Noop, Delete: resourceMacSecKeyDelete, Importer: &schema.ResourceImporter{ @@ -30,12 +33,14 @@ func ResourceMacSecKey() *schema.Resource { Optional: true, // CAK requires CKN RequiredWith: []string{"ckn"}, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`[a-fA-F0-9]{64}$`), "Must be 64-character hex code string"), }, "ckn": { Type: schema.TypeString, Computed: true, Optional: true, AtLeastOneOf: []string{"ckn", "secret_arn"}, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`[a-fA-F0-9]{64}$`), "Must be 64-character hex code string"), }, "connection_id": { Type: schema.TypeString, @@ -63,10 +68,16 @@ func resourceMacSecKeyCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).DirectConnectConn input := &directconnect.AssociateMacSecKeyInput{ - Cak: aws.String(d.Get("cak").(string)), - Ckn: aws.String(d.Get("ckn").(string)), ConnectionId: aws.String(d.Get("connection_id").(string)), - SecretARN: aws.String(d.Get("secret_arn").(string)), + } + + if d.Get("ckn").(string) != "" { + input.Cak = aws.String(d.Get("cak").(string)) + input.Ckn = aws.String(d.Get("ckn").(string)) + } + + if d.Get("secret_arn").(string) != "" { + input.SecretARN = aws.String(d.Get("secret_arn").(string)) } log.Printf("[DEBUG] Creating MACSec secret key on Direct Connect Connection: %s", *input.ConnectionId) @@ -79,7 +90,9 @@ func resourceMacSecKeyCreate(d *schema.ResourceData, meta interface{}) error { secret_arn := MacSecKeyParseSecretArn(output) // Create a composite ID based on connection ID and secret ARN - d.SetId(fmt.Sprintf("%s:%s", secret_arn, aws.StringValue(output.ConnectionId))) + d.SetId(fmt.Sprintf("%s/%s", secret_arn, aws.StringValue(output.ConnectionId))) + + d.Set("secret_arn", secret_arn) return nil } @@ -92,11 +105,11 @@ func resourceMacSecKeyDelete(d *schema.ResourceData, meta interface{}) error { SecretARN: aws.String(d.Get("secret_arn").(string)), } - log.Printf("[DEBUG] Deleting MACSec secret key on Direct Connect Connection: %s", *input.ConnectionId) + log.Printf("[DEBUG] Disassociating MACSec secret key on Direct Connect Connection: %s", *input.ConnectionId) _, err := conn.DisassociateMacSecKey(input) if err != nil { - return fmt.Errorf("error delecting MACSec secret key on Direct Connect Connection (%s): %w", *input.ConnectionId, err) + return fmt.Errorf("Unable to disassociate MACSec secret key on Direct Connect Connection (%s): %w", *input.ConnectionId, err) } return nil @@ -117,12 +130,12 @@ func MacSecKeyParseSecretArn(output *directconnect.AssociateMacSecKeyOutput) str return result } -// Define an exported function to parse the resource ID +// MacSecKeyParseID parses the resource ID and returns the secret ARN and connection ID func MacSecKeyParseID(id string) (string, string, error) { - parts := strings.SplitN(id, ":", 2) + parts := strings.SplitN(id, "/", 2) if len(parts) != 2 || parts[0] == "" || parts[1] == "" { - return "", "", fmt.Errorf("unexpected format of ID (%s), expected :connectionId", id) + return "", "", fmt.Errorf("unexpected format of ID (%s), expected secretArn:connectionId", id) } return parts[0], parts[1], nil diff --git a/internal/service/directconnect/macsec_key_test.go b/internal/service/directconnect/macsec_key_test.go new file mode 100644 index 00000000000..b713703241b --- /dev/null +++ b/internal/service/directconnect/macsec_key_test.go @@ -0,0 +1,149 @@ +package directconnect_test + +import ( + "crypto/rand" + "encoding/hex" + "fmt" + "os" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" +) + +// func TestAccDirectConnectMacSecKey_withSecret(t *testing.T) { +// var connection directconnect.Connection +// resourceName := "aws_dx_macsec_key.test" +// dxResourceName := "aws_dx_connection.test" +// ckn := testAccDirecConnectMacSecGenerateHex() +// cak := testAccDirecConnectMacSecGenerateHex() +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(t) }, +// ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), +// ProviderFactories: acctest.ProviderFactories, +// CheckDestroy: testAccCheckConnectionDestroy, +// Steps: []resource.TestStep{ +// { +// Config: testAccDirectConnectMacSecConfig_withSecret(rName, ckn, cak), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckConnectionExists(dxResourceName, &connection), +// // TODO: check that DX connection ID and MacSec connection ID match +// // resource.TestCheckResourceAttrPair(resourceName, "connection_id", dxResourceName, "id"), +// // TODO: check that MacSecKey exists on DX connection +// // resource.TestMatchResourceAttr(dxResourceName, "macsec_keys", regexp.MustCompile(``)), +// ), +// }, +// // Test import. +// { +// ResourceName: resourceName, +// ImportState: true, +// ImportStateVerify: true, +// }, +// }, +// }) +// } + +func TestAccDirectConnectMacSecKey_withCkn(t *testing.T) { + // Requires an existing MACsec-capable DX connection set as environmental variable + key := "DX_CONNECTION_ID" + connectionId := os.Getenv(key) + if connectionId == "" { + t.Skipf("Environment variable %s is not set", key) + } + resourceName := "aws_dx_macsec_key.test" + ckn := testAccDirecConnectMacSecGenerateHex() + cak := testAccDirecConnectMacSecGenerateHex() + // rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + // ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccDirectConnectMacSecConfig_withCkn(ckn, cak, connectionId), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestMatchResourceAttr(resourceName, "ckn", regexp.MustCompile(ckn)), + ), + }, + }, + }) +} + +func TestAccDirectConnectMacSecKey_withSecret(t *testing.T) { + // Requires an existing MACsec-capable DX connection set as environmental variable + dxKey := "DX_CONNECTION_ID" + connectionId := os.Getenv(dxKey) + if connectionId == "" { + t.Skipf("Environment variable %s is not set", dxKey) + } + + secretKey := "SECRET_ARN" + secretArn := os.Getenv(secretKey) + if secretArn == "" { + t.Skipf("Environment variable %s is not set", secretKey) + } + + resourceName := "aws_dx_macsec_key.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + // ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccDirectConnectMacSecConfig_withSecret(secretArn, connectionId), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttr(resourceName, "secret_arn", secretArn), + ), + }, + }, + }) +} + +// testAccDirecConnectMacSecGenerateKey generates a 64-character hex string to be used as CKN or CAK +func testAccDirecConnectMacSecGenerateHex() string { + + s := make([]byte, 32) + if _, err := rand.Read(s); err != nil { + return "" + } + return hex.EncodeToString(s) +} + +func testAccDirectConnectMacSecConfig_withCkn(ckn, cak, connectionId string) string { + conf := fmt.Sprintf(` +resource "aws_dx_macsec_key" "test" { + connection_id = %[3]q + ckn = %[1]q + cak = %[2]q +} + +`, ckn, cak, connectionId) + fmt.Println(conf) + return conf +} + +// Can only be used with an EXISTING secrets - cannot create secrets from scratch +func testAccDirectConnectMacSecConfig_withSecret(secretArn, connectionId string) string { + conf := fmt.Sprintf(` +data "aws_secretsmanager_secret" "test" { + arn = %[1]q +} + +resource "aws_dx_macsec_key" "test" { + connection_id = %[2]q + secret_arn = data.aws_secretsmanager_secret.test.arn +} + +`, secretArn, connectionId) + fmt.Println(conf) + return conf +} From 6738f1f4aba5ba631f6f6d9ee21158e9778e6f54 Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Fri, 12 Aug 2022 14:00:25 -0400 Subject: [PATCH 03/30] Add changelog, run acc test linting --- .changelog/26274.txt | 7 +++ .../service/directconnect/connection_test.go | 6 +-- .../service/directconnect/macsec_key_test.go | 51 +++---------------- 3 files changed, 17 insertions(+), 47 deletions(-) create mode 100644 .changelog/26274.txt diff --git a/.changelog/26274.txt b/.changelog/26274.txt new file mode 100644 index 00000000000..e2d41beb339 --- /dev/null +++ b/.changelog/26274.txt @@ -0,0 +1,7 @@ +```release-note:new-resource +aws_dx_macsec_key +``` + +```release-note:enhancement +resource/aws_dx_connection: Add arguments to enable MACsec support +``` \ No newline at end of file diff --git a/internal/service/directconnect/connection_test.go b/internal/service/directconnect/connection_test.go index 98008f34659..03314df39e0 100644 --- a/internal/service/directconnect/connection_test.go +++ b/internal/service/directconnect/connection_test.go @@ -273,9 +273,9 @@ data "aws_dx_location" "test" { } resource "aws_dx_connection" "test" { - name = %[1]q - bandwidth = "100Gbps" - location = data.aws_dx_location.test.location_code + name = %[1]q + bandwidth = "100Gbps" + location = data.aws_dx_location.test.location_code macsec_requested = true provider_name = data.aws_dx_location.test.available_providers[0] diff --git a/internal/service/directconnect/macsec_key_test.go b/internal/service/directconnect/macsec_key_test.go index b713703241b..ce2462c95b5 100644 --- a/internal/service/directconnect/macsec_key_test.go +++ b/internal/service/directconnect/macsec_key_test.go @@ -12,40 +12,6 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" ) -// func TestAccDirectConnectMacSecKey_withSecret(t *testing.T) { -// var connection directconnect.Connection -// resourceName := "aws_dx_macsec_key.test" -// dxResourceName := "aws_dx_connection.test" -// ckn := testAccDirecConnectMacSecGenerateHex() -// cak := testAccDirecConnectMacSecGenerateHex() -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(t) }, -// ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), -// ProviderFactories: acctest.ProviderFactories, -// CheckDestroy: testAccCheckConnectionDestroy, -// Steps: []resource.TestStep{ -// { -// Config: testAccDirectConnectMacSecConfig_withSecret(rName, ckn, cak), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckConnectionExists(dxResourceName, &connection), -// // TODO: check that DX connection ID and MacSec connection ID match -// // resource.TestCheckResourceAttrPair(resourceName, "connection_id", dxResourceName, "id"), -// // TODO: check that MacSecKey exists on DX connection -// // resource.TestMatchResourceAttr(dxResourceName, "macsec_keys", regexp.MustCompile(``)), -// ), -// }, -// // Test import. -// { -// ResourceName: resourceName, -// ImportState: true, -// ImportStateVerify: true, -// }, -// }, -// }) -// } - func TestAccDirectConnectMacSecKey_withCkn(t *testing.T) { // Requires an existing MACsec-capable DX connection set as environmental variable key := "DX_CONNECTION_ID" @@ -56,7 +22,6 @@ func TestAccDirectConnectMacSecKey_withCkn(t *testing.T) { resourceName := "aws_dx_macsec_key.test" ckn := testAccDirecConnectMacSecGenerateHex() cak := testAccDirecConnectMacSecGenerateHex() - // rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -119,31 +84,29 @@ func testAccDirecConnectMacSecGenerateHex() string { } func testAccDirectConnectMacSecConfig_withCkn(ckn, cak, connectionId string) string { - conf := fmt.Sprintf(` + return fmt.Sprintf(` resource "aws_dx_macsec_key" "test" { connection_id = %[3]q - ckn = %[1]q - cak = %[2]q + ckn = %[1]q + cak = %[2]q } + `, ckn, cak, connectionId) - fmt.Println(conf) - return conf } // Can only be used with an EXISTING secrets - cannot create secrets from scratch func testAccDirectConnectMacSecConfig_withSecret(secretArn, connectionId string) string { - conf := fmt.Sprintf(` + return fmt.Sprintf(` data "aws_secretsmanager_secret" "test" { arn = %[1]q } resource "aws_dx_macsec_key" "test" { connection_id = %[2]q - secret_arn = data.aws_secretsmanager_secret.test.arn + secret_arn = data.aws_secretsmanager_secret.test.arn } + `, secretArn, connectionId) - fmt.Println(conf) - return conf } From a1c5b07b56343d89b4e0a9e4a03a6d9ab15967aa Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Tue, 23 Aug 2022 16:22:19 -0400 Subject: [PATCH 04/30] [WIP] Add and update docs - Create docs for resource `aws_dx_macsec_key` - [WIP] Update existing docs for resource `aws_dx_connection` --- website/docs/r/dx_connection.html.markdown | 2 + website/docs/r/dx_macsec_key.html.markdown | 68 ++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 website/docs/r/dx_macsec_key.html.markdown diff --git a/website/docs/r/dx_connection.html.markdown b/website/docs/r/dx_connection.html.markdown index d5ffd598898..8e60b5cf18b 100644 --- a/website/docs/r/dx_connection.html.markdown +++ b/website/docs/r/dx_connection.html.markdown @@ -26,6 +26,8 @@ The following arguments are supported: * `bandwidth` - (Required) The bandwidth of the connection. Valid values for dedicated connections: 1Gbps, 10Gbps. Valid values for hosted connections: 50Mbps, 100Mbps, 200Mbps, 300Mbps, 400Mbps, 500Mbps, 1Gbps, 2Gbps, 5Gbps, 10Gbps and 100Gbps. Case sensitive. * `location` - (Required) The AWS Direct Connect location where the connection is located. See [DescribeLocations](https://docs.aws.amazon.com/directconnect/latest/APIReference/API_DescribeLocations.html) for the list of AWS Direct Connect locations. Use `locationCode`. +* `macsec_requested` - (Optional) Boolean value indicating whether you want the connection to support MAC Security (MACsec). MAC Security (MACsec) is only available on dedicated connections. See [MACsec prerequisites](https://docs.aws.amazon.com/directconnect/latest/UserGuide/direct-connect-mac-sec-getting-started.html#mac-sec-prerequisites) for information +about MAC Security (MACsec) prerequisties. Default value: `false`. * `name` - (Required) The name of the connection. * `provider_name` - (Optional) The name of the service provider associated with the connection. * `tags` - (Optional) A map of tags to assign to the resource. 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. diff --git a/website/docs/r/dx_macsec_key.html.markdown b/website/docs/r/dx_macsec_key.html.markdown new file mode 100644 index 00000000000..5cdeef698c1 --- /dev/null +++ b/website/docs/r/dx_macsec_key.html.markdown @@ -0,0 +1,68 @@ +--- +subcategory: "Direct Connect" +layout: "aws" +page_title: "AWS: aws_dx_macsec_key" +description: |- + Provides a MAC Security (MACSec) secret key resource for use with Direct Connect. +--- + +# Resource: aws_macsec_key + +Provides a MAC Security (MACSec) secret key resource for use with Direct Connect. See [MACsec prerequisites](https://docs.aws.amazon.com/directconnect/latest/UserGuide/direct-connect-mac-sec-getting-started.html#mac-sec-prerequisites) for information about MAC Security (MACsec) prerequisites. + +Creating this resource will also create a resource of type [`aws_secretsmanager_secret`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret) which is managed by Direct Connect. While you can import this resource into your Terraform state, because this secret is managed by Direct Connect, you will not be able to make any modifications to it. See [How AWS Direct Connect uses AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/integrating_how-services-use-secrets_directconnect.html) for details. + +~> **Note:** All arguments including `ckn` and `cak` will be stored in the raw state as plain-text. +[Read more about sensitive data in state](https://www.terraform.io/docs/state/sensitive-data.html). + +~> **Note:** The `secret_arn` argument can only be used to reference a previously created MACSec key. You cannot associate a Secrets Manager secret created outside of the `aws_dx_macsec_key` resource. + +## Example Usage + +### Create MACSec key with CKN and CAK +```terraform +data "aws_dx_connection" "example" { + name = "tf-dx-connection" +} + +resource "aws_dx_macsec_key" "test" { + connection_id = data.aws_dx_connection.example.id + ckn = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" + cak = "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789" +} +``` + +### Create MACSec key with existing Secrets Manager secret +```terraform +data "aws_dx_connection" "example" { + name = "tf-dx-connection" +} + +data "aws_secretsmanager_secret" "example" { + name = "directconnect!prod/us-east-1/directconnect/0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" +} + +resource "aws_dx_macsec_key" "test" { + connection_id = data.aws_dx_connection.example.id + secret_arn = data.aws_secretsmanager_secret.example.arn +} +``` + +## Argument Reference + +The following arguments are supported: + +* `cak` - (Optional) The MAC Security (MACsec) CAK to associate with the dedicated connection. The valid values are 64 hexadecimal characters (0-9, A-E). Required if using `ckn`. +* `ckn` - (Optional) The MAC Security (MACsec) CKN to associate with the dedicated connection. The valid values are 64 hexadecimal characters (0-9, A-E). Required if using `cak`. +* `connection_id` - (Required) The ID of the dedicated Direct Connect connection. The connection must be a dedicated connection in the `AVAILABLE` state. +* `secret_arn` - (Optional) The Amazon Resource Name (ARN) of the MAC Security (MACsec) secret key to associate with the dedicated connection. + +~> **Note:** `ckn` and `cak` are mutually exclusive with `secret_arn` - these arguments cannot be used together. If you use `ckn` and `cak`, you should not use `secret_arn`. If you use the `secret_arn` argument to reference an existing MAC Security (MACSec) secret key, you should not use `ckn` or `cak`. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - ID of the MAC Security (MACSec) secret key resource. +* `start_on` - The date in UTC format that the MAC Security (MACsec) secret key takes effect. +* `state` - The state of the MAC Security (MACsec) secret key. The possible values are: associating, associated, disassociating, disassociated. See [MacSecKey](https://docs.aws.amazon.com/directconnect/latest/APIReference/API_MacSecKey.html#DX-Type-MacSecKey-state) for descriptions of each state. \ No newline at end of file From 45071a7128dd8b13eebc92d152ce6d9dc22433bb Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Thu, 25 Aug 2022 10:55:54 -0400 Subject: [PATCH 05/30] Minor doc/linting fixes --- internal/service/directconnect/macsec_key.go | 4 ++-- website/docs/r/dx_connection.html.markdown | 2 +- website/docs/r/dx_macsec_key.html.markdown | 8 +++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/internal/service/directconnect/macsec_key.go b/internal/service/directconnect/macsec_key.go index 664363736bf..54b362b0473 100644 --- a/internal/service/directconnect/macsec_key.go +++ b/internal/service/directconnect/macsec_key.go @@ -87,7 +87,7 @@ func resourceMacSecKeyCreate(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("error creating MACSec secret key on Direct Connect Connection (%s): %w", *input.ConnectionId, err) } - secret_arn := MacSecKeyParseSecretArn(output) + secret_arn := MacSecKeyParseSecretARN(output) // Create a composite ID based on connection ID and secret ARN d.SetId(fmt.Sprintf("%s/%s", secret_arn, aws.StringValue(output.ConnectionId))) @@ -116,7 +116,7 @@ func resourceMacSecKeyDelete(d *schema.ResourceData, meta interface{}) error { } // MacSecKeyParseSecretArn parses the secret ARN returned from a CMK or secret_arn -func MacSecKeyParseSecretArn(output *directconnect.AssociateMacSecKeyOutput) string { +func MacSecKeyParseSecretARN(output *directconnect.AssociateMacSecKeyOutput) string { var result string for _, key := range output.MacSecKeys { diff --git a/website/docs/r/dx_connection.html.markdown b/website/docs/r/dx_connection.html.markdown index 8e60b5cf18b..13f2f5852d0 100644 --- a/website/docs/r/dx_connection.html.markdown +++ b/website/docs/r/dx_connection.html.markdown @@ -27,7 +27,7 @@ The following arguments are supported: * `bandwidth` - (Required) The bandwidth of the connection. Valid values for dedicated connections: 1Gbps, 10Gbps. Valid values for hosted connections: 50Mbps, 100Mbps, 200Mbps, 300Mbps, 400Mbps, 500Mbps, 1Gbps, 2Gbps, 5Gbps, 10Gbps and 100Gbps. Case sensitive. * `location` - (Required) The AWS Direct Connect location where the connection is located. See [DescribeLocations](https://docs.aws.amazon.com/directconnect/latest/APIReference/API_DescribeLocations.html) for the list of AWS Direct Connect locations. Use `locationCode`. * `macsec_requested` - (Optional) Boolean value indicating whether you want the connection to support MAC Security (MACsec). MAC Security (MACsec) is only available on dedicated connections. See [MACsec prerequisites](https://docs.aws.amazon.com/directconnect/latest/UserGuide/direct-connect-mac-sec-getting-started.html#mac-sec-prerequisites) for information -about MAC Security (MACsec) prerequisties. Default value: `false`. +about MAC Security (MACsec) prerequisites. Default value: `false`. * `name` - (Required) The name of the connection. * `provider_name` - (Optional) The name of the service provider associated with the connection. * `tags` - (Optional) A map of tags to assign to the resource. 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. diff --git a/website/docs/r/dx_macsec_key.html.markdown b/website/docs/r/dx_macsec_key.html.markdown index 5cdeef698c1..dfdddf24cd6 100644 --- a/website/docs/r/dx_macsec_key.html.markdown +++ b/website/docs/r/dx_macsec_key.html.markdown @@ -6,7 +6,7 @@ description: |- Provides a MAC Security (MACSec) secret key resource for use with Direct Connect. --- -# Resource: aws_macsec_key +# Resource: aws_dx_macsec_key Provides a MAC Security (MACSec) secret key resource for use with Direct Connect. See [MACsec prerequisites](https://docs.aws.amazon.com/directconnect/latest/UserGuide/direct-connect-mac-sec-getting-started.html#mac-sec-prerequisites) for information about MAC Security (MACsec) prerequisites. @@ -20,6 +20,7 @@ Creating this resource will also create a resource of type [`aws_secretsmanager_ ## Example Usage ### Create MACSec key with CKN and CAK + ```terraform data "aws_dx_connection" "example" { name = "tf-dx-connection" @@ -33,6 +34,7 @@ resource "aws_dx_macsec_key" "test" { ``` ### Create MACSec key with existing Secrets Manager secret + ```terraform data "aws_dx_connection" "example" { name = "tf-dx-connection" @@ -44,7 +46,7 @@ data "aws_secretsmanager_secret" "example" { resource "aws_dx_macsec_key" "test" { connection_id = data.aws_dx_connection.example.id - secret_arn = data.aws_secretsmanager_secret.example.arn + secret_arn = data.aws_secretsmanager_secret.example.arn } ``` @@ -65,4 +67,4 @@ In addition to all arguments above, the following attributes are exported: * `id` - ID of the MAC Security (MACSec) secret key resource. * `start_on` - The date in UTC format that the MAC Security (MACsec) secret key takes effect. -* `state` - The state of the MAC Security (MACsec) secret key. The possible values are: associating, associated, disassociating, disassociated. See [MacSecKey](https://docs.aws.amazon.com/directconnect/latest/APIReference/API_MacSecKey.html#DX-Type-MacSecKey-state) for descriptions of each state. \ No newline at end of file +* `state` - The state of the MAC Security (MACsec) secret key. The possible values are: associating, associated, disassociating, disassociated. See [MacSecKey](https://docs.aws.amazon.com/directconnect/latest/APIReference/API_MacSecKey.html#DX-Type-MacSecKey-state) for descriptions of each state. From dd223d208b60d8238bd3e46a05349f8fc60f6b6a Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Thu, 25 Aug 2022 12:02:39 -0400 Subject: [PATCH 06/30] Add support to delete keys on disassociate --- internal/service/directconnect/macsec_key.go | 25 +++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/internal/service/directconnect/macsec_key.go b/internal/service/directconnect/macsec_key.go index 54b362b0473..ad32f9898d3 100644 --- a/internal/service/directconnect/macsec_key.go +++ b/internal/service/directconnect/macsec_key.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/aws/aws-sdk-go/service/secretsmanager" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -112,10 +113,32 @@ func resourceMacSecKeyDelete(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Unable to disassociate MACSec secret key on Direct Connect Connection (%s): %w", *input.ConnectionId, err) } + // Disassociating the key does not delete it from Secrets Manager, do that here + err = resourceMacSecKeySecretDelete(*input.SecretARN, meta) + if err != nil { + return fmt.Errorf("Unable to delete MACSec secret key %s: %w", *input.SecretARN, err) + } + + return nil +} + +func resourceMacSecKeySecretDelete(secretId string, meta interface{}) error { + conn := meta.(*conns.AWSClient).SecretsManagerConn + input := &secretsmanager.DeleteSecretInput{ + SecretId: aws.String(secretId), + } + + log.Printf("[DEBUG] Deleting MACSec secret key: %s", *input.SecretId) + _, err := conn.DeleteSecret(input) + + if err != nil { + return err + } + return nil } -// MacSecKeyParseSecretArn parses the secret ARN returned from a CMK or secret_arn +// MacSecKeyParseSecretARN parses the secret ARN returned from a CMK or secret_arn func MacSecKeyParseSecretARN(output *directconnect.AssociateMacSecKeyOutput) string { var result string From 4ca85dfeaeeaa4ed33243a29acf93d264684bebb Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Thu, 1 Sep 2022 15:53:59 -0400 Subject: [PATCH 07/30] Add read function to macsec_key resource --- internal/service/directconnect/connection.go | 1 - internal/service/directconnect/macsec_key.go | 32 +++++++++++++++++-- .../service/directconnect/macsec_key_test.go | 10 ++++++ 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/internal/service/directconnect/connection.go b/internal/service/directconnect/connection.go index 38d5f18f975..43c38f4854d 100644 --- a/internal/service/directconnect/connection.go +++ b/internal/service/directconnect/connection.go @@ -194,7 +194,6 @@ func resourceConnectionRead(d *schema.ResourceData, meta interface{}) error { d.Set("jumbo_frame_capable", connection.JumboFrameCapable) d.Set("location", connection.Location) d.Set("macsec_capable", connection.MacSecCapable) - // d.Set("macsec_keys", connection.MacSecKeys) d.Set("name", connection.ConnectionName) d.Set("owner_account_id", connection.OwnerAccount) d.Set("port_encryption_status", connection.PortEncryptionStatus) diff --git a/internal/service/directconnect/macsec_key.go b/internal/service/directconnect/macsec_key.go index ad32f9898d3..255c95fb6b2 100644 --- a/internal/service/directconnect/macsec_key.go +++ b/internal/service/directconnect/macsec_key.go @@ -16,9 +16,9 @@ import ( func ResourceMacSecKey() *schema.Resource { return &schema.Resource{ - // MacSecKey resource only supports create (Associate) and delete (Disassociate) + // MacSecKey resource only supports create (Associate), read (Describe) and delete (Disassociate) Create: resourceMacSecKeyCreate, - Read: schema.Noop, + Read: resourceMacSecKeyRead, // You cannot modify a MACsec secret key after you associate it with a connection. // To modify the key, disassociate the key from the connection, and then associate // a new key with the connection @@ -98,6 +98,34 @@ func resourceMacSecKeyCreate(d *schema.ResourceData, meta interface{}) error { return nil } +func resourceMacSecKeyRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).DirectConnectConn + + secretArn, connId, err := MacSecKeyParseID(d.Id()) + + connection, err := FindConnectionByID(conn, connId) + + if err != nil { + return fmt.Errorf("error reading Direct Connect Connection (%s): %w", d.Id(), err) + } + + if connection.MacSecKeys != nil { + for _, key := range connection.MacSecKeys { + if key.SecretARN == &secretArn { + d.Set("ckn", key.Ckn) + d.Set("connection_id", connId) + d.Set("secret_arn", key.SecretARN) + d.Set("start_on", key.StartOn) + d.Set("state", key.State) + } + } + } else { + return fmt.Errorf("No MACSec keys found on Direct Connect Connection (%s)", d.Id()) + } + + return nil +} + func resourceMacSecKeyDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).DirectConnectConn diff --git a/internal/service/directconnect/macsec_key_test.go b/internal/service/directconnect/macsec_key_test.go index ce2462c95b5..8def5953bfd 100644 --- a/internal/service/directconnect/macsec_key_test.go +++ b/internal/service/directconnect/macsec_key_test.go @@ -36,6 +36,11 @@ func TestAccDirectConnectMacSecKey_withCkn(t *testing.T) { resource.TestMatchResourceAttr(resourceName, "ckn", regexp.MustCompile(ckn)), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -69,6 +74,11 @@ func TestAccDirectConnectMacSecKey_withSecret(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "secret_arn", secretArn), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } From 96a5dd5cb1e182c1b4e83bfaff582637044675e4 Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Thu, 1 Sep 2022 21:08:54 -0400 Subject: [PATCH 08/30] Update acceptance tests, clean up dx_connection resource --- internal/service/directconnect/connection.go | 73 ------------------- internal/service/directconnect/macsec_key.go | 37 +++++----- .../service/directconnect/macsec_key_test.go | 13 ++-- 3 files changed, 28 insertions(+), 95 deletions(-) diff --git a/internal/service/directconnect/connection.go b/internal/service/directconnect/connection.go index 43c38f4854d..0549cc149fd 100644 --- a/internal/service/directconnect/connection.go +++ b/internal/service/directconnect/connection.go @@ -199,27 +199,6 @@ func resourceConnectionRead(d *schema.ResourceData, meta interface{}) error { d.Set("port_encryption_status", connection.PortEncryptionStatus) d.Set("provider_name", connection.ProviderName) - // if connection.MacSecKeys != nil { - // keys := make([]interface{}, len(connection.MacSecKeys)) - // for i, key := range connection.MacSecKeys { - // k := map[string]interface{}{ - // "Ckn": aws.StringValue(key.Ckn), - // "SecretARN": aws.StringValue(key.SecretARN), - // "StartOn": aws.StringValue(key.StartOn), - // "State": aws.StringValue(key.State), - // } - // fmt.Println(k) - // keys[i] = k - // } - // d.Set("macsec_keys", keys) - // } - - // fmt.Println("MACSec keys:", connection.MacSecKeys) - - // if err := d.Set("macsec_keys", flattenMacSecKeys(connection.MacSecKeys)); err != nil { - // return fmt.Errorf("error setting macsec_keys: %s", err) - // } - tags, err := ListTags(conn, arn) if err != nil { @@ -326,55 +305,3 @@ func ValidateMacSecAvailability(l string, p string, meta interface{}) bool { } return available } - -// Expand and flatten structures for MACSec keys -// Ref: https://github.com/hashicorp/terraform-provider-aws/blob/main/docs/contributing/data-handling-and-conversion.md#flatten-functions-for-blocks - -func flattenMacSecKey(macSecKey *directconnect.MacSecKey) map[string]interface{} { - if macSecKey == nil { - return nil - } - - keyMap := map[string]interface{}{} - - // nested attribute handling - - return keyMap -} - -func flattenMacSecKeyStructures(macSecKeys []*directconnect.MacSecKey) []interface{} { - if len(macSecKeys) == 0 { - return nil - } - - var keyList []interface{} - - for _, key := range macSecKeys { - if key == nil { - continue - } - - keyList = append(keyList, flattenMacSecKey(key)) - } - return keyList - - // if macSecKeys == nil { - // return []interface{}{} - // } - - // // fmt.Println("Length of MACSeckeys:", len(macSecKeys)) - - // keys := make([]interface{}, len(macSecKeys)) - // for i, key := range macSecKeys { - // k := map[string]interface{}{ - // "Ckn": aws.StringValue(key.Ckn), - // "SecretARN": aws.StringValue(key.SecretARN), - // "StartOn": aws.StringValue(key.StartOn), - // "State": aws.StringValue(key.State), - // } - // fmt.Println(k) - // keys[i] = k - // } - - // return []interface{}{keys} -} diff --git a/internal/service/directconnect/macsec_key.go b/internal/service/directconnect/macsec_key.go index 255c95fb6b2..18dd8434643 100644 --- a/internal/service/directconnect/macsec_key.go +++ b/internal/service/directconnect/macsec_key.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directconnect" "github.com/aws/aws-sdk-go/service/secretsmanager" + "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/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -91,38 +92,40 @@ func resourceMacSecKeyCreate(d *schema.ResourceData, meta interface{}) error { secret_arn := MacSecKeyParseSecretARN(output) // Create a composite ID based on connection ID and secret ARN - d.SetId(fmt.Sprintf("%s/%s", secret_arn, aws.StringValue(output.ConnectionId))) + d.SetId(fmt.Sprintf("%s_%s", secret_arn, aws.StringValue(output.ConnectionId))) d.Set("secret_arn", secret_arn) - return nil + return resourceMacSecKeyRead(d, meta) } func resourceMacSecKeyRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).DirectConnectConn secretArn, connId, err := MacSecKeyParseID(d.Id()) + if err != nil { + return fmt.Errorf("unexpected format of ID (%s), expected secretArn_connectionId", d.Id()) + } connection, err := FindConnectionByID(conn, connId) - if err != nil { return fmt.Errorf("error reading Direct Connect Connection (%s): %w", d.Id(), err) } - if connection.MacSecKeys != nil { - for _, key := range connection.MacSecKeys { - if key.SecretARN == &secretArn { - d.Set("ckn", key.Ckn) - d.Set("connection_id", connId) - d.Set("secret_arn", key.SecretARN) - d.Set("start_on", key.StartOn) - d.Set("state", key.State) - } - } - } else { + if connection.MacSecKeys == nil { return fmt.Errorf("No MACSec keys found on Direct Connect Connection (%s)", d.Id()) } + for _, key := range connection.MacSecKeys { + if aws.StringValue(key.SecretARN) == aws.StringValue(&secretArn) { + d.Set("ckn", key.Ckn) + d.Set("connection_id", connId) + d.Set("secret_arn", key.SecretARN) + d.Set("start_on", key.StartOn) + d.Set("state", key.State) + } + } + return nil } @@ -144,7 +147,7 @@ func resourceMacSecKeyDelete(d *schema.ResourceData, meta interface{}) error { // Disassociating the key does not delete it from Secrets Manager, do that here err = resourceMacSecKeySecretDelete(*input.SecretARN, meta) if err != nil { - return fmt.Errorf("Unable to delete MACSec secret key %s: %w", *input.SecretARN, err) + return err } return nil @@ -183,10 +186,10 @@ func MacSecKeyParseSecretARN(output *directconnect.AssociateMacSecKeyOutput) str // MacSecKeyParseID parses the resource ID and returns the secret ARN and connection ID func MacSecKeyParseID(id string) (string, string, error) { - parts := strings.SplitN(id, "/", 2) + parts := strings.SplitN(id, "_", 2) if len(parts) != 2 || parts[0] == "" || parts[1] == "" { - return "", "", fmt.Errorf("unexpected format of ID (%s), expected secretArn:connectionId", id) + return "", "", &resource.NotFoundError{} } return parts[0], parts[1], nil diff --git a/internal/service/directconnect/macsec_key_test.go b/internal/service/directconnect/macsec_key_test.go index 8def5953bfd..96c72fb4fda 100644 --- a/internal/service/directconnect/macsec_key_test.go +++ b/internal/service/directconnect/macsec_key_test.go @@ -8,6 +8,7 @@ import ( "regexp" "testing" + "github.com/aws/aws-sdk-go/service/directconnect" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/acctest" ) @@ -24,8 +25,8 @@ func TestAccDirectConnectMacSecKey_withCkn(t *testing.T) { cak := testAccDirecConnectMacSecGenerateHex() resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, - // ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), ProviderFactories: acctest.ProviderFactories, CheckDestroy: nil, Steps: []resource.TestStep{ @@ -40,6 +41,8 @@ func TestAccDirectConnectMacSecKey_withCkn(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, + // Ignore the "cak" attribute as isn't returned by the API during read/refresh + ImportStateVerifyIgnore: []string{"cak"}, }, }, }) @@ -62,8 +65,8 @@ func TestAccDirectConnectMacSecKey_withSecret(t *testing.T) { resourceName := "aws_dx_macsec_key.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, - // ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), ProviderFactories: acctest.ProviderFactories, CheckDestroy: nil, Steps: []resource.TestStep{ @@ -105,7 +108,7 @@ resource "aws_dx_macsec_key" "test" { `, ckn, cak, connectionId) } -// Can only be used with an EXISTING secrets - cannot create secrets from scratch +// Can only be used with an EXISTING secrets created by previous association - cannot create secrets from scratch func testAccDirectConnectMacSecConfig_withSecret(secretArn, connectionId string) string { return fmt.Sprintf(` data "aws_secretsmanager_secret" "test" { From c23c8f1190cb04e4f7a8252d94c511cdd3db858e Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Thu, 1 Sep 2022 21:14:43 -0400 Subject: [PATCH 09/30] Minor linting fixes --- internal/service/directconnect/connection.go | 30 ------------------- .../service/directconnect/macsec_key_test.go | 8 ++--- 2 files changed, 4 insertions(+), 34 deletions(-) diff --git a/internal/service/directconnect/connection.go b/internal/service/directconnect/connection.go index 0549cc149fd..5db5a9b977b 100644 --- a/internal/service/directconnect/connection.go +++ b/internal/service/directconnect/connection.go @@ -275,33 +275,3 @@ func deleteConnection(conn *directconnect.DirectConnect, connectionID string, wa return nil } - -// ValidateMacSecAvailability checks for MAC Security availability given a -// location `l` and desired port speed `p` -func ValidateMacSecAvailability(l string, p string, meta interface{}) bool { - conn := meta.(*conns.AWSClient).DirectConnectConn - input := &directconnect.DescribeLocationsInput{} - - response, err := conn.DescribeLocations(input) - - if err != nil { - fmt.Printf("error checking MACSec availability: %s", err) - return false - } - - var available bool - - for _, location := range response.Locations { - if l == *location.LocationCode { - for _, port_speed := range location.AvailableMacSecPortSpeeds { - if *port_speed == p { - available = true - } else { - fmt.Printf("MACSec not available for location %s and desired port speed %s", l, p) - available = false - } - } - } - } - return available -} diff --git a/internal/service/directconnect/macsec_key_test.go b/internal/service/directconnect/macsec_key_test.go index 96c72fb4fda..7c167baa8f9 100644 --- a/internal/service/directconnect/macsec_key_test.go +++ b/internal/service/directconnect/macsec_key_test.go @@ -31,7 +31,7 @@ func TestAccDirectConnectMacSecKey_withCkn(t *testing.T) { CheckDestroy: nil, Steps: []resource.TestStep{ { - Config: testAccDirectConnectMacSecConfig_withCkn(ckn, cak, connectionId), + Config: testAccMacSecConfig_withCkn(ckn, cak, connectionId), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), resource.TestMatchResourceAttr(resourceName, "ckn", regexp.MustCompile(ckn)), @@ -71,7 +71,7 @@ func TestAccDirectConnectMacSecKey_withSecret(t *testing.T) { CheckDestroy: nil, Steps: []resource.TestStep{ { - Config: testAccDirectConnectMacSecConfig_withSecret(secretArn, connectionId), + Config: testAccMacSecConfig_withSecret(secretArn, connectionId), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), resource.TestCheckResourceAttr(resourceName, "secret_arn", secretArn), @@ -96,7 +96,7 @@ func testAccDirecConnectMacSecGenerateHex() string { return hex.EncodeToString(s) } -func testAccDirectConnectMacSecConfig_withCkn(ckn, cak, connectionId string) string { +func testAccMacSecConfig_withCkn(ckn, cak, connectionId string) string { return fmt.Sprintf(` resource "aws_dx_macsec_key" "test" { connection_id = %[3]q @@ -109,7 +109,7 @@ resource "aws_dx_macsec_key" "test" { } // Can only be used with an EXISTING secrets created by previous association - cannot create secrets from scratch -func testAccDirectConnectMacSecConfig_withSecret(secretArn, connectionId string) string { +func testAccMacSecConfig_withSecret(secretArn, connectionId string) string { return fmt.Sprintf(` data "aws_secretsmanager_secret" "test" { arn = %[1]q From 063abff6346e9af7e9392aad244a7ad29a62258b Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Thu, 15 Sep 2022 10:45:17 -0400 Subject: [PATCH 10/30] Add sweeper for aws_dx_macsec_keys, remove secret delete function - Remove default "secret delete" behavior to align with API behavior - Add sweeper for MACsec keys to clean up dangling acceptance test resources --- internal/service/directconnect/macsec_key.go | 23 ------- internal/service/directconnect/sweep.go | 65 ++++++++++++++++++++ 2 files changed, 65 insertions(+), 23 deletions(-) diff --git a/internal/service/directconnect/macsec_key.go b/internal/service/directconnect/macsec_key.go index 18dd8434643..1883afc773d 100644 --- a/internal/service/directconnect/macsec_key.go +++ b/internal/service/directconnect/macsec_key.go @@ -8,7 +8,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/aws/aws-sdk-go/service/secretsmanager" "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/helper/validation" @@ -144,28 +143,6 @@ func resourceMacSecKeyDelete(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Unable to disassociate MACSec secret key on Direct Connect Connection (%s): %w", *input.ConnectionId, err) } - // Disassociating the key does not delete it from Secrets Manager, do that here - err = resourceMacSecKeySecretDelete(*input.SecretARN, meta) - if err != nil { - return err - } - - return nil -} - -func resourceMacSecKeySecretDelete(secretId string, meta interface{}) error { - conn := meta.(*conns.AWSClient).SecretsManagerConn - input := &secretsmanager.DeleteSecretInput{ - SecretId: aws.String(secretId), - } - - log.Printf("[DEBUG] Deleting MACSec secret key: %s", *input.SecretId) - _, err := conn.DeleteSecret(input) - - if err != nil { - return err - } - return nil } diff --git a/internal/service/directconnect/sweep.go b/internal/service/directconnect/sweep.go index c76dc024189..a7c20b5941a 100644 --- a/internal/service/directconnect/sweep.go +++ b/internal/service/directconnect/sweep.go @@ -10,6 +10,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directconnect" "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/secretsmanager" "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -48,6 +49,12 @@ func init() { F: sweepLags, Dependencies: []string{"aws_dx_connection"}, }) + + resource.AddTestSweepers("aws_dx_macsec_key", &resource.Sweeper{ + Name: "aws_dx_macsec_key", + F: sweepMacSecKeys, + Dependencies: []string{}, + }) } func sweepConnections(region string) error { @@ -439,3 +446,61 @@ func sweepLags(region string) error { return sweeperErrs.ErrorOrNil() } + +func sweepMacSecKeys(region string) error { + client, err := sweep.SharedRegionalSweepClient(region) + if err != nil { + return fmt.Errorf("error getting client: %w", err) + } + + dxConn := client.(*conns.AWSClient).DirectConnectConn + + // Clean up leaked Secrets Manager resources created by Direct Connect. + // Direct Connect does not remove the corresponding Secrets Manager + // key when deleting the MACsec key association. The only option to + // clean up the dangling resource is to use Secrets Manager to delete + // the MACsec key secret. + smConn := client.(*conns.AWSClient).SecretsManagerConn + dxInput := &directconnect.DescribeConnectionsInput{} + var sweeperErrs *multierror.Error + + output, err := dxConn.DescribeConnections(dxInput) + + if err != nil { + sweeperErr := fmt.Errorf("error listing Direct Connect Connections for %s: %w", region, err) + log.Printf("[ERROR] %s", sweeperErr) + sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) + return sweeperErrs.ErrorOrNil() + } + + if output == nil { + log.Printf("[WARN] Skipping Direct Connect MACsec Keys sweep for %s: empty response", region) + return sweeperErrs.ErrorOrNil() + } + + for _, connection := range output.Connections { + if connection.MacSecKeys == nil { + continue + } + + for _, key := range connection.MacSecKeys { + arn := aws.StringValue(key.SecretArn) + + input := &secretsmanager.DeleteSecretInput{ + SecretId: aws.String(arn), + } + + log.Printf("[DEBUG] Deleting MACSec secret key: %s", *input.SecretId) + _, err := smConn.DeleteSecret(input) + + if err != nil { + sweeperErr := fmt.Errorf("error deleting MACsec Secret (%s): %w", arn, err) + log.Printf("[ERROR] %s", sweeperErr) + sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) + continue + } + } + } + + return sweeperErrs.ErrorOrNil() +} From 810926c368253108f9f84b3f1e0bb56efee9ddf1 Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Thu, 15 Sep 2022 11:27:41 -0400 Subject: [PATCH 11/30] Update aws_dx_connection documentation, fix sweeper attribute error --- internal/service/directconnect/sweep.go | 2 +- website/docs/r/dx_connection.html.markdown | 35 ++++++++++++++++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/internal/service/directconnect/sweep.go b/internal/service/directconnect/sweep.go index a7c20b5941a..607e5195c3b 100644 --- a/internal/service/directconnect/sweep.go +++ b/internal/service/directconnect/sweep.go @@ -484,7 +484,7 @@ func sweepMacSecKeys(region string) error { } for _, key := range connection.MacSecKeys { - arn := aws.StringValue(key.SecretArn) + arn := aws.StringValue(key.SecretARN) input := &secretsmanager.DeleteSecretInput{ SecretId: aws.String(arn), diff --git a/website/docs/r/dx_connection.html.markdown b/website/docs/r/dx_connection.html.markdown index 13f2f5852d0..6a6f042b26a 100644 --- a/website/docs/r/dx_connection.html.markdown +++ b/website/docs/r/dx_connection.html.markdown @@ -12,6 +12,7 @@ Provides a Connection of Direct Connect. ## Example Usage +### Create a connection ```terraform resource "aws_dx_connection" "hoge" { name = "tf-dx-connection" @@ -20,14 +21,40 @@ resource "aws_dx_connection" "hoge" { } ``` +### Request a MACsec-capable connection +```terraform +resource "aws_dx_connection" "example" { + name = "tf-dx-connection" + bandwidth = "10Gbps" + location = "EqDA2" + macsec_requested = true +} +``` + +### Configure encryption mode for MACsec-capable connections +-> **NOTE:** You can only specify the `encryption_mode` argument once the connection is in an `Available` state. + +```terraform +resource "aws_dx_connection" "example" { + name = "tf-dx-connection" + bandwidth = "10Gbps" + location = "EqDC2" + macsec_requested = true + encryption_mode = "must_encrypt" +} +``` + ## Argument Reference The following arguments are supported: * `bandwidth` - (Required) The bandwidth of the connection. Valid values for dedicated connections: 1Gbps, 10Gbps. Valid values for hosted connections: 50Mbps, 100Mbps, 200Mbps, 300Mbps, 400Mbps, 500Mbps, 1Gbps, 2Gbps, 5Gbps, 10Gbps and 100Gbps. Case sensitive. +* `encryption_mode` - (Optional) The connection MAC Security (MACsec) encryption mode. MAC Security (MACsec) is only available on dedicated connections. Valid values are `no_encrypt`, `should_encrypt`, and `must_encrypt`. * `location` - (Required) The AWS Direct Connect location where the connection is located. See [DescribeLocations](https://docs.aws.amazon.com/directconnect/latest/APIReference/API_DescribeLocations.html) for the list of AWS Direct Connect locations. Use `locationCode`. -* `macsec_requested` - (Optional) Boolean value indicating whether you want the connection to support MAC Security (MACsec). MAC Security (MACsec) is only available on dedicated connections. See [MACsec prerequisites](https://docs.aws.amazon.com/directconnect/latest/UserGuide/direct-connect-mac-sec-getting-started.html#mac-sec-prerequisites) for information -about MAC Security (MACsec) prerequisites. Default value: `false`. +* `macsec_requested` - (Optional) Boolean value indicating whether you want the connection to support MAC Security (MACsec). MAC Security (MACsec) is only available on dedicated connections. See [MACsec prerequisites](https://docs.aws.amazon.com/directconnect/latest/UserGuide/direct-connect-mac-sec-getting-started.html#mac-sec-prerequisites) for more information about MAC Security (MACsec) prerequisites. Default value: `false`. + +~> **NOTE:** Changing the value of `macsec_requested` will cause the resource to be destroyed and re-created. + * `name` - (Required) The name of the connection. * `provider_name` - (Optional) The name of the service provider associated with the connection. * `tags` - (Optional) A map of tags to assign to the resource. 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. @@ -41,8 +68,10 @@ In addition to all arguments above, the following attributes are exported: * `has_logical_redundancy` - Indicates whether the connection supports a secondary BGP peer in the same address family (IPv4/IPv6). * `id` - The ID of the connection. * `jumbo_frame_capable` - Boolean value representing if jumbo frames have been enabled for this connection. +* `macsec_capable` - Boolean value indicating whether the connection supports MAC Security (MACsec). * `owner_account_id` - The ID of the AWS account that owns the connection. -* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). +* `port_encryption_status` - The MAC Security (MACsec) port link status of the connection. +* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block). ## Import From ae5cc7f292a8415380754361bc9b1852d6883671 Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Thu, 15 Sep 2022 12:07:49 -0400 Subject: [PATCH 12/30] Remove unneeded schema items, fix docs conflict --- internal/service/directconnect/connection.go | 27 -------------------- website/docs/r/dx_connection.html.markdown | 2 +- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/internal/service/directconnect/connection.go b/internal/service/directconnect/connection.go index 5db5a9b977b..283b4a80040 100644 --- a/internal/service/directconnect/connection.go +++ b/internal/service/directconnect/connection.go @@ -73,33 +73,6 @@ func ResourceConnection() *schema.Resource { Default: false, ForceNew: true, }, - // The MAC Security (MACsec) security keys associated with the connection. - "macsec_keys": { - // Slice of type MacSecKey - Type: schema.TypeList, - Computed: true, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "secret_arn": { - Type: schema.TypeString, - Computed: true, - }, - "ckn": { - Type: schema.TypeString, - Computed: true, - }, - "state": { - Type: schema.TypeString, - Computed: true, - }, - "start_on": { - Type: schema.TypeString, - Computed: true, - }, - }, - }, - }, "name": { Type: schema.TypeString, Required: true, diff --git a/website/docs/r/dx_connection.html.markdown b/website/docs/r/dx_connection.html.markdown index 6a6f042b26a..158393be338 100644 --- a/website/docs/r/dx_connection.html.markdown +++ b/website/docs/r/dx_connection.html.markdown @@ -71,7 +71,7 @@ In addition to all arguments above, the following attributes are exported: * `macsec_capable` - Boolean value indicating whether the connection supports MAC Security (MACsec). * `owner_account_id` - The ID of the AWS account that owns the connection. * `port_encryption_status` - The MAC Security (MACsec) port link status of the connection. -* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block). +* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). ## Import From c12eecc9e6636f363f4e40ec9f1317db69d5da9e Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Thu, 15 Sep 2022 13:54:44 -0400 Subject: [PATCH 13/30] Minor linting fixes --- .../service/directconnect/connection_test.go | 8 ++++---- internal/service/directconnect/macsec_key.go | 2 +- .../service/directconnect/macsec_key_test.go | 16 ++++++++-------- website/docs/r/dx_connection.html.markdown | 14 +++++++------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/internal/service/directconnect/connection_test.go b/internal/service/directconnect/connection_test.go index 03314df39e0..afa93200f97 100644 --- a/internal/service/directconnect/connection_test.go +++ b/internal/service/directconnect/connection_test.go @@ -78,10 +78,10 @@ func TestAccDirectConnectConnection_macsecRequested(t *testing.T) { rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, - ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), - ProviderFactories: acctest.ProviderFactories, - CheckDestroy: testAccCheckConnectionDestroy, + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckConnectionDestroy, Steps: []resource.TestStep{ { Config: testAccConnectionConfig_macsecEnabled(rName), diff --git a/internal/service/directconnect/macsec_key.go b/internal/service/directconnect/macsec_key.go index 1883afc773d..0d3ad5661a5 100644 --- a/internal/service/directconnect/macsec_key.go +++ b/internal/service/directconnect/macsec_key.go @@ -112,7 +112,7 @@ func resourceMacSecKeyRead(d *schema.ResourceData, meta interface{}) error { } if connection.MacSecKeys == nil { - return fmt.Errorf("No MACSec keys found on Direct Connect Connection (%s)", d.Id()) + return fmt.Errorf("no MACSec keys found on Direct Connect Connection (%s)", d.Id()) } for _, key := range connection.MacSecKeys { diff --git a/internal/service/directconnect/macsec_key_test.go b/internal/service/directconnect/macsec_key_test.go index 7c167baa8f9..e04cec11ab7 100644 --- a/internal/service/directconnect/macsec_key_test.go +++ b/internal/service/directconnect/macsec_key_test.go @@ -25,10 +25,10 @@ func TestAccDirectConnectMacSecKey_withCkn(t *testing.T) { cak := testAccDirecConnectMacSecGenerateHex() resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, - ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), - ProviderFactories: acctest.ProviderFactories, - CheckDestroy: nil, + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: nil, Steps: []resource.TestStep{ { Config: testAccMacSecConfig_withCkn(ckn, cak, connectionId), @@ -65,10 +65,10 @@ func TestAccDirectConnectMacSecKey_withSecret(t *testing.T) { resourceName := "aws_dx_macsec_key.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, - ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), - ProviderFactories: acctest.ProviderFactories, - CheckDestroy: nil, + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: nil, Steps: []resource.TestStep{ { Config: testAccMacSecConfig_withSecret(secretArn, connectionId), diff --git a/website/docs/r/dx_connection.html.markdown b/website/docs/r/dx_connection.html.markdown index 158393be338..207a6ef9598 100644 --- a/website/docs/r/dx_connection.html.markdown +++ b/website/docs/r/dx_connection.html.markdown @@ -24,9 +24,9 @@ resource "aws_dx_connection" "hoge" { ### Request a MACsec-capable connection ```terraform resource "aws_dx_connection" "example" { - name = "tf-dx-connection" - bandwidth = "10Gbps" - location = "EqDA2" + name = "tf-dx-connection" + bandwidth = "10Gbps" + location = "EqDA2" macsec_requested = true } ``` @@ -36,11 +36,11 @@ resource "aws_dx_connection" "example" { ```terraform resource "aws_dx_connection" "example" { - name = "tf-dx-connection" - bandwidth = "10Gbps" - location = "EqDC2" + name = "tf-dx-connection" + bandwidth = "10Gbps" + location = "EqDC2" macsec_requested = true - encryption_mode = "must_encrypt" + encryption_mode = "must_encrypt" } ``` From 143007aa295502f539eb4531dcb8f6a1dede9dd9 Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Thu, 15 Sep 2022 15:04:31 -0400 Subject: [PATCH 14/30] Additional linting fixes --- internal/service/directconnect/macsec_key.go | 2 +- website/docs/r/dx_connection.html.markdown | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/service/directconnect/macsec_key.go b/internal/service/directconnect/macsec_key.go index 0d3ad5661a5..db2c8aa0ec4 100644 --- a/internal/service/directconnect/macsec_key.go +++ b/internal/service/directconnect/macsec_key.go @@ -155,7 +155,7 @@ func MacSecKeyParseSecretARN(output *directconnect.AssociateMacSecKeyOutput) str continue } if key != nil { - result = *key.SecretARN + result = aws.StringValue(key.SecretARN) } } return result diff --git a/website/docs/r/dx_connection.html.markdown b/website/docs/r/dx_connection.html.markdown index 207a6ef9598..2de57dfd080 100644 --- a/website/docs/r/dx_connection.html.markdown +++ b/website/docs/r/dx_connection.html.markdown @@ -13,6 +13,7 @@ Provides a Connection of Direct Connect. ## Example Usage ### Create a connection + ```terraform resource "aws_dx_connection" "hoge" { name = "tf-dx-connection" @@ -22,6 +23,7 @@ resource "aws_dx_connection" "hoge" { ``` ### Request a MACsec-capable connection + ```terraform resource "aws_dx_connection" "example" { name = "tf-dx-connection" @@ -49,7 +51,7 @@ resource "aws_dx_connection" "example" { The following arguments are supported: * `bandwidth` - (Required) The bandwidth of the connection. Valid values for dedicated connections: 1Gbps, 10Gbps. Valid values for hosted connections: 50Mbps, 100Mbps, 200Mbps, 300Mbps, 400Mbps, 500Mbps, 1Gbps, 2Gbps, 5Gbps, 10Gbps and 100Gbps. Case sensitive. -* `encryption_mode` - (Optional) The connection MAC Security (MACsec) encryption mode. MAC Security (MACsec) is only available on dedicated connections. Valid values are `no_encrypt`, `should_encrypt`, and `must_encrypt`. +* `encryption_mode` - (Optional) The connection MAC Security (MACsec) encryption mode. MAC Security (MACsec) is only available on dedicated connections. Valid values are `no_encrypt`, `should_encrypt`, and `must_encrypt`. * `location` - (Required) The AWS Direct Connect location where the connection is located. See [DescribeLocations](https://docs.aws.amazon.com/directconnect/latest/APIReference/API_DescribeLocations.html) for the list of AWS Direct Connect locations. Use `locationCode`. * `macsec_requested` - (Optional) Boolean value indicating whether you want the connection to support MAC Security (MACsec). MAC Security (MACsec) is only available on dedicated connections. See [MACsec prerequisites](https://docs.aws.amazon.com/directconnect/latest/UserGuide/direct-connect-mac-sec-getting-started.html#mac-sec-prerequisites) for more information about MAC Security (MACsec) prerequisites. Default value: `false`. From b07f4724423a6af22d124ec66a8edd94f28101ac Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Thu, 15 Dec 2022 11:06:42 -0500 Subject: [PATCH 15/30] Apply code review suggestion: `request_macsec` attribute Update attribute name to align with API Co-authored-by: Albert Silva <50742829+silvaalbert@users.noreply.github.com> --- internal/service/directconnect/connection.go | 6 +++--- internal/service/directconnect/connection_test.go | 4 ++-- website/docs/r/dx_connection.html.markdown | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/service/directconnect/connection.go b/internal/service/directconnect/connection.go index 283b4a80040..aaa0b6d7e23 100644 --- a/internal/service/directconnect/connection.go +++ b/internal/service/directconnect/connection.go @@ -67,7 +67,7 @@ func ResourceConnection() *schema.Resource { Computed: true, }, // Enable or disable MAC Security (MACsec) on this connection. - "macsec_requested": { + "request_macsec": { Type: schema.TypeBool, Optional: true, Default: false, @@ -111,7 +111,7 @@ func resourceConnectionCreate(d *schema.ResourceData, meta interface{}) error { Bandwidth: aws.String(d.Get("bandwidth").(string)), ConnectionName: aws.String(name), Location: aws.String(d.Get("location").(string)), - RequestMACSec: aws.Bool(d.Get("macsec_requested").(bool)), + RequestMACSec: aws.Bool(d.Get("request_macsec").(bool)), } if v, ok := d.GetOk("provider_name"); ok { @@ -130,7 +130,7 @@ func resourceConnectionCreate(d *schema.ResourceData, meta interface{}) error { } d.SetId(aws.StringValue(output.ConnectionId)) - d.Set("macsec_requested", d.Get("macsec_requested").(bool)) + d.Set("request_macsec", d.Get("request_macsec").(bool)) return resourceConnectionRead(d, meta) } diff --git a/internal/service/directconnect/connection_test.go b/internal/service/directconnect/connection_test.go index afa93200f97..29c206e72b5 100644 --- a/internal/service/directconnect/connection_test.go +++ b/internal/service/directconnect/connection_test.go @@ -92,7 +92,7 @@ func TestAccDirectConnectConnection_macsecRequested(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "location"), // macsec_capable will not return "true" while connection is in "Requesting" state resource.TestCheckResourceAttr(resourceName, "macsec_capable", "false"), - resource.TestCheckResourceAttr(resourceName, "macsec_requested", "true"), + resource.TestCheckResourceAttr(resourceName, "request_macsec", "true"), acctest.CheckResourceAttrAccountID(resourceName, "owner_account_id"), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttrSet(resourceName, "provider_name"), @@ -276,7 +276,7 @@ resource "aws_dx_connection" "test" { name = %[1]q bandwidth = "100Gbps" location = data.aws_dx_location.test.location_code - macsec_requested = true + request_macsec = true provider_name = data.aws_dx_location.test.available_providers[0] } diff --git a/website/docs/r/dx_connection.html.markdown b/website/docs/r/dx_connection.html.markdown index 2de57dfd080..21a40137075 100644 --- a/website/docs/r/dx_connection.html.markdown +++ b/website/docs/r/dx_connection.html.markdown @@ -29,7 +29,7 @@ resource "aws_dx_connection" "example" { name = "tf-dx-connection" bandwidth = "10Gbps" location = "EqDA2" - macsec_requested = true + request_macsec = true } ``` @@ -41,7 +41,7 @@ resource "aws_dx_connection" "example" { name = "tf-dx-connection" bandwidth = "10Gbps" location = "EqDC2" - macsec_requested = true + request_macsec = true encryption_mode = "must_encrypt" } ``` @@ -53,7 +53,7 @@ The following arguments are supported: * `bandwidth` - (Required) The bandwidth of the connection. Valid values for dedicated connections: 1Gbps, 10Gbps. Valid values for hosted connections: 50Mbps, 100Mbps, 200Mbps, 300Mbps, 400Mbps, 500Mbps, 1Gbps, 2Gbps, 5Gbps, 10Gbps and 100Gbps. Case sensitive. * `encryption_mode` - (Optional) The connection MAC Security (MACsec) encryption mode. MAC Security (MACsec) is only available on dedicated connections. Valid values are `no_encrypt`, `should_encrypt`, and `must_encrypt`. * `location` - (Required) The AWS Direct Connect location where the connection is located. See [DescribeLocations](https://docs.aws.amazon.com/directconnect/latest/APIReference/API_DescribeLocations.html) for the list of AWS Direct Connect locations. Use `locationCode`. -* `macsec_requested` - (Optional) Boolean value indicating whether you want the connection to support MAC Security (MACsec). MAC Security (MACsec) is only available on dedicated connections. See [MACsec prerequisites](https://docs.aws.amazon.com/directconnect/latest/UserGuide/direct-connect-mac-sec-getting-started.html#mac-sec-prerequisites) for more information about MAC Security (MACsec) prerequisites. Default value: `false`. +* `request_macsec` - (Optional) Boolean value indicating whether you want the connection to support MAC Security (MACsec). MAC Security (MACsec) is only available on dedicated connections. See [MACsec prerequisites](https://docs.aws.amazon.com/directconnect/latest/UserGuide/direct-connect-mac-sec-getting-started.html#mac-sec-prerequisites) for more information about MAC Security (MACsec) prerequisites. Default value: `false`. ~> **NOTE:** Changing the value of `macsec_requested` will cause the resource to be destroyed and re-created. From a72afad65300090d0362115678c9de16b178db7d Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Thu, 15 Dec 2022 13:02:09 -0500 Subject: [PATCH 16/30] Add ForceNew to required attribute, remove update Noop --- internal/service/directconnect/macsec_key.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/service/directconnect/macsec_key.go b/internal/service/directconnect/macsec_key.go index db2c8aa0ec4..5ccace21ac8 100644 --- a/internal/service/directconnect/macsec_key.go +++ b/internal/service/directconnect/macsec_key.go @@ -19,10 +19,6 @@ func ResourceMacSecKey() *schema.Resource { // MacSecKey resource only supports create (Associate), read (Describe) and delete (Disassociate) Create: resourceMacSecKeyCreate, Read: resourceMacSecKeyRead, - // You cannot modify a MACsec secret key after you associate it with a connection. - // To modify the key, disassociate the key from the connection, and then associate - // a new key with the connection - Update: schema.Noop, Delete: resourceMacSecKeyDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, @@ -35,6 +31,7 @@ func ResourceMacSecKey() *schema.Resource { // CAK requires CKN RequiredWith: []string{"ckn"}, ValidateFunc: validation.StringMatch(regexp.MustCompile(`[a-fA-F0-9]{64}$`), "Must be 64-character hex code string"), + ForceNew: true, }, "ckn": { Type: schema.TypeString, @@ -42,6 +39,7 @@ func ResourceMacSecKey() *schema.Resource { Optional: true, AtLeastOneOf: []string{"ckn", "secret_arn"}, ValidateFunc: validation.StringMatch(regexp.MustCompile(`[a-fA-F0-9]{64}$`), "Must be 64-character hex code string"), + ForceNew: true, }, "connection_id": { Type: schema.TypeString, @@ -52,6 +50,7 @@ func ResourceMacSecKey() *schema.Resource { Optional: true, Computed: true, AtLeastOneOf: []string{"ckn", "secret_arn"}, + ForceNew: true, }, "start_on": { Type: schema.TypeString, From 6f984deb82121ebb215fe7736835252cd4306693 Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Thu, 15 Dec 2022 13:24:52 -0500 Subject: [PATCH 17/30] Add ForceNew to macsec connection, remove check on macsec_capable attr --- internal/service/directconnect/connection_test.go | 4 +--- internal/service/directconnect/macsec_key.go | 7 ++++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/internal/service/directconnect/connection_test.go b/internal/service/directconnect/connection_test.go index 1adb2306899..33bbc832019 100644 --- a/internal/service/directconnect/connection_test.go +++ b/internal/service/directconnect/connection_test.go @@ -91,8 +91,6 @@ func TestAccDirectConnectConnection_macsecRequested(t *testing.T) { acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(`dxcon/.+`)), resource.TestCheckResourceAttr(resourceName, "bandwidth", "100Gbps"), resource.TestCheckResourceAttrSet(resourceName, "location"), - // macsec_capable will not return "true" while connection is in "Requesting" state - resource.TestCheckResourceAttr(resourceName, "macsec_capable", "false"), resource.TestCheckResourceAttr(resourceName, "request_macsec", "true"), acctest.CheckResourceAttrAccountID(resourceName, "owner_account_id"), resource.TestCheckResourceAttr(resourceName, "name", rName), @@ -105,7 +103,7 @@ func TestAccDirectConnectConnection_macsecRequested(t *testing.T) { ImportState: true, ImportStateVerify: true, // Ignore the "macsec_requested" attribute as isn't returned by the API during read/refresh - ImportStateVerifyIgnore: []string{"macsec_requested"}, + ImportStateVerifyIgnore: []string{"request_macsec"}, }, }, }) diff --git a/internal/service/directconnect/macsec_key.go b/internal/service/directconnect/macsec_key.go index 5ccace21ac8..310ebe74046 100644 --- a/internal/service/directconnect/macsec_key.go +++ b/internal/service/directconnect/macsec_key.go @@ -31,7 +31,7 @@ func ResourceMacSecKey() *schema.Resource { // CAK requires CKN RequiredWith: []string{"ckn"}, ValidateFunc: validation.StringMatch(regexp.MustCompile(`[a-fA-F0-9]{64}$`), "Must be 64-character hex code string"), - ForceNew: true, + ForceNew: true, }, "ckn": { Type: schema.TypeString, @@ -39,18 +39,19 @@ func ResourceMacSecKey() *schema.Resource { Optional: true, AtLeastOneOf: []string{"ckn", "secret_arn"}, ValidateFunc: validation.StringMatch(regexp.MustCompile(`[a-fA-F0-9]{64}$`), "Must be 64-character hex code string"), - ForceNew: true, + ForceNew: true, }, "connection_id": { Type: schema.TypeString, Required: true, + ForceNew: true, }, "secret_arn": { Type: schema.TypeString, Optional: true, Computed: true, AtLeastOneOf: []string{"ckn", "secret_arn"}, - ForceNew: true, + ForceNew: true, }, "start_on": { Type: schema.TypeString, From db13c885d7f00b03eae64b7b3226777dc9386c14 Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Thu, 15 Dec 2022 13:29:27 -0500 Subject: [PATCH 18/30] Rerun Terrafmt linting --- .../service/directconnect/connection_test.go | 6 +++--- website/docs/r/dx_connection.html.markdown | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/internal/service/directconnect/connection_test.go b/internal/service/directconnect/connection_test.go index 33bbc832019..8a307b6aed8 100644 --- a/internal/service/directconnect/connection_test.go +++ b/internal/service/directconnect/connection_test.go @@ -272,9 +272,9 @@ data "aws_dx_location" "test" { } resource "aws_dx_connection" "test" { - name = %[1]q - bandwidth = "100Gbps" - location = data.aws_dx_location.test.location_code + name = %[1]q + bandwidth = "100Gbps" + location = data.aws_dx_location.test.location_code request_macsec = true provider_name = data.aws_dx_location.test.available_providers[0] diff --git a/website/docs/r/dx_connection.html.markdown b/website/docs/r/dx_connection.html.markdown index 56f6a1ddf9c..d85cfd592ff 100644 --- a/website/docs/r/dx_connection.html.markdown +++ b/website/docs/r/dx_connection.html.markdown @@ -26,9 +26,9 @@ resource "aws_dx_connection" "hoge" { ```terraform resource "aws_dx_connection" "example" { - name = "tf-dx-connection" - bandwidth = "10Gbps" - location = "EqDA2" + name = "tf-dx-connection" + bandwidth = "10Gbps" + location = "EqDA2" request_macsec = true } ``` @@ -38,11 +38,11 @@ resource "aws_dx_connection" "example" { ```terraform resource "aws_dx_connection" "example" { - name = "tf-dx-connection" - bandwidth = "10Gbps" - location = "EqDC2" - request_macsec = true - encryption_mode = "must_encrypt" + name = "tf-dx-connection" + bandwidth = "10Gbps" + location = "EqDC2" + request_macsec = true + encryption_mode = "must_encrypt" } ``` From 047756f8004d0e6f7c467a76cbb6c0e3ebec68e3 Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Thu, 15 Dec 2022 13:35:39 -0500 Subject: [PATCH 19/30] Apply CR suggestion: rename to `aws_dx_macsec_key_association` Co-authored-by: Albert Silva <50742829+silvaalbert@users.noreply.github.com> --- internal/service/directconnect/macsec_key.go | 2 +- internal/service/directconnect/macsec_key_test.go | 8 ++++---- website/docs/r/dx_macsec_key.html.markdown | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/service/directconnect/macsec_key.go b/internal/service/directconnect/macsec_key.go index 310ebe74046..7a13c5bc81e 100644 --- a/internal/service/directconnect/macsec_key.go +++ b/internal/service/directconnect/macsec_key.go @@ -14,7 +14,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" ) -func ResourceMacSecKey() *schema.Resource { +func ResourceMacSecKeyAssociation() *schema.Resource { return &schema.Resource{ // MacSecKey resource only supports create (Associate), read (Describe) and delete (Disassociate) Create: resourceMacSecKeyCreate, diff --git a/internal/service/directconnect/macsec_key_test.go b/internal/service/directconnect/macsec_key_test.go index e04cec11ab7..ed89ab52957 100644 --- a/internal/service/directconnect/macsec_key_test.go +++ b/internal/service/directconnect/macsec_key_test.go @@ -20,7 +20,7 @@ func TestAccDirectConnectMacSecKey_withCkn(t *testing.T) { if connectionId == "" { t.Skipf("Environment variable %s is not set", key) } - resourceName := "aws_dx_macsec_key.test" + resourceName := "aws_dx_macsec_key_association.test" ckn := testAccDirecConnectMacSecGenerateHex() cak := testAccDirecConnectMacSecGenerateHex() @@ -62,7 +62,7 @@ func TestAccDirectConnectMacSecKey_withSecret(t *testing.T) { t.Skipf("Environment variable %s is not set", secretKey) } - resourceName := "aws_dx_macsec_key.test" + resourceName := "aws_dx_macsec_key_association.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -98,7 +98,7 @@ func testAccDirecConnectMacSecGenerateHex() string { func testAccMacSecConfig_withCkn(ckn, cak, connectionId string) string { return fmt.Sprintf(` -resource "aws_dx_macsec_key" "test" { +resource "aws_dx_macsec_key_association" "test" { connection_id = %[3]q ckn = %[1]q cak = %[2]q @@ -115,7 +115,7 @@ data "aws_secretsmanager_secret" "test" { arn = %[1]q } -resource "aws_dx_macsec_key" "test" { +resource "aws_dx_macsec_key_association" "test" { connection_id = %[2]q secret_arn = data.aws_secretsmanager_secret.test.arn } diff --git a/website/docs/r/dx_macsec_key.html.markdown b/website/docs/r/dx_macsec_key.html.markdown index dfdddf24cd6..505d3f461ba 100644 --- a/website/docs/r/dx_macsec_key.html.markdown +++ b/website/docs/r/dx_macsec_key.html.markdown @@ -1,12 +1,12 @@ --- subcategory: "Direct Connect" layout: "aws" -page_title: "AWS: aws_dx_macsec_key" +page_title: "AWS: aws_dx_macsec_key_association" description: |- Provides a MAC Security (MACSec) secret key resource for use with Direct Connect. --- -# Resource: aws_dx_macsec_key +# Resource: aws_dx_macsec_key_association Provides a MAC Security (MACSec) secret key resource for use with Direct Connect. See [MACsec prerequisites](https://docs.aws.amazon.com/directconnect/latest/UserGuide/direct-connect-mac-sec-getting-started.html#mac-sec-prerequisites) for information about MAC Security (MACsec) prerequisites. @@ -26,7 +26,7 @@ data "aws_dx_connection" "example" { name = "tf-dx-connection" } -resource "aws_dx_macsec_key" "test" { +resource "aws_dx_macsec_key_association" "test" { connection_id = data.aws_dx_connection.example.id ckn = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" cak = "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789" @@ -44,7 +44,7 @@ data "aws_secretsmanager_secret" "example" { name = "directconnect!prod/us-east-1/directconnect/0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" } -resource "aws_dx_macsec_key" "test" { +resource "aws_dx_macsec_key_association" "test" { connection_id = data.aws_dx_connection.example.id secret_arn = data.aws_secretsmanager_secret.example.arn } From 8ad97cbaec53fb2d14d13610832d910ddd323e31 Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Thu, 15 Dec 2022 13:36:45 -0500 Subject: [PATCH 20/30] Update internal/provider/provider.go Co-authored-by: Albert Silva <50742829+silvaalbert@users.noreply.github.com> --- internal/provider/provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 01913e52a73..b3d8debc020 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1304,7 +1304,7 @@ func New(_ context.Context) (*schema.Provider, error) { "aws_dx_hosted_transit_virtual_interface": directconnect.ResourceHostedTransitVirtualInterface(), "aws_dx_hosted_transit_virtual_interface_accepter": directconnect.ResourceHostedTransitVirtualInterfaceAccepter(), "aws_dx_lag": directconnect.ResourceLag(), - "aws_dx_macsec_key": directconnect.ResourceMacSecKey(), + "aws_dx_macsec_key_association": directconnect.ResourceMacSecKeyAssociation(), "aws_dx_private_virtual_interface": directconnect.ResourcePrivateVirtualInterface(), "aws_dx_public_virtual_interface": directconnect.ResourcePublicVirtualInterface(), "aws_dx_transit_virtual_interface": directconnect.ResourceTransitVirtualInterface(), From fd0aa9d59885fc3ebd1cd6540d83cc2b99fd67d7 Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Thu, 15 Dec 2022 15:22:22 -0500 Subject: [PATCH 21/30] Rename docs, add encryption_mode acc test [WIP] --- internal/provider/provider.go | 2 +- .../service/directconnect/connection_test.go | 60 +++++++++++++++++++ ...> dx_macsec_key_association.html.markdown} | 0 3 files changed, 61 insertions(+), 1 deletion(-) rename website/docs/r/{dx_macsec_key.html.markdown => dx_macsec_key_association.html.markdown} (100%) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index b3d8debc020..9d2e42792e8 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1304,7 +1304,7 @@ func New(_ context.Context) (*schema.Provider, error) { "aws_dx_hosted_transit_virtual_interface": directconnect.ResourceHostedTransitVirtualInterface(), "aws_dx_hosted_transit_virtual_interface_accepter": directconnect.ResourceHostedTransitVirtualInterfaceAccepter(), "aws_dx_lag": directconnect.ResourceLag(), - "aws_dx_macsec_key_association": directconnect.ResourceMacSecKeyAssociation(), + "aws_dx_macsec_key_association": directconnect.ResourceMacSecKeyAssociation(), "aws_dx_private_virtual_interface": directconnect.ResourcePrivateVirtualInterface(), "aws_dx_public_virtual_interface": directconnect.ResourcePublicVirtualInterface(), "aws_dx_transit_virtual_interface": directconnect.ResourceTransitVirtualInterface(), diff --git a/internal/service/directconnect/connection_test.go b/internal/service/directconnect/connection_test.go index 8a307b6aed8..119b83c7a56 100644 --- a/internal/service/directconnect/connection_test.go +++ b/internal/service/directconnect/connection_test.go @@ -2,6 +2,7 @@ package directconnect_test import ( "fmt" + "os" "regexp" "testing" @@ -73,6 +74,43 @@ func TestAccDirectConnectConnection_disappears(t *testing.T) { }) } +func TestAccDirectConnectConnection_encryptionMode(t *testing.T) { + dxKey := "DX_CONNECTION_ID" + connectionId := os.Getenv(dxKey) + if connectionId == "" { + t.Skipf("Environment variable %s is not set", dxKey) + } + + dxName := "DX_CONNECTION_NAME" + connectionName := os.Getenv(dxName) + if connectionName == "" { + t.Skipf("Environment variable %s is not set", connectionName) + } + + resourceName := "aws_dx_connection.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccConnectionConfig_encryptionMode(connectionName), + ResourceName: resourceName, + ImportState: true, + ImportStateId: connectionId, + ImportStatePersist: true, + }, + { + Config: testAccConnectionConfig_encryptionModeMustEncrypt(resourceName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "encryption_mode", "must_encrypt"), + ), + }, + }, + }) +} + func TestAccDirectConnectConnection_macsecRequested(t *testing.T) { var connection directconnect.Connection resourceName := "aws_dx_connection.test" @@ -258,6 +296,28 @@ resource "aws_dx_connection" "test" { `, rName) } +func testAccConnectionConfig_encryptionModeMustEncrypt(rName string) string { + return fmt.Sprintf(` +resource "aws_dx_connection" "test" { + name = %[1]q + location = "CSOW" + bandwidth = "100Gbps" + encryption_mode = "must_encrypt" +} +`, rName) +} + +func testAccConnectionConfig_encryptionMode(rName string) string { + return fmt.Sprintf(` +resource "aws_dx_connection" "test" { + name = %[1]q + location = "CSOW" + bandwidth = "100Gbps" + encryption_mode = "should_encrypt" + } +`, rName) +} + func testAccConnectionConfig_macsecEnabled(rName string) string { return fmt.Sprintf(` data "aws_dx_locations" "test" {} diff --git a/website/docs/r/dx_macsec_key.html.markdown b/website/docs/r/dx_macsec_key_association.html.markdown similarity index 100% rename from website/docs/r/dx_macsec_key.html.markdown rename to website/docs/r/dx_macsec_key_association.html.markdown From 3e236d96cdf1021f7bb7b8d9bea8ba1ea34a9ff3 Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Fri, 16 Dec 2022 13:19:31 -0500 Subject: [PATCH 22/30] Add skip_destroy argument for aws_dx_connection --- .changelog/26274.txt | 3 +- internal/service/directconnect/connection.go | 10 +++ .../service/directconnect/connection_test.go | 72 +++++++++++++++++-- website/docs/r/dx_connection.html.markdown | 7 +- 4 files changed, 81 insertions(+), 11 deletions(-) diff --git a/.changelog/26274.txt b/.changelog/26274.txt index e2d41beb339..9145117b744 100644 --- a/.changelog/26274.txt +++ b/.changelog/26274.txt @@ -1,7 +1,8 @@ ```release-note:new-resource -aws_dx_macsec_key +aws_dx_macsec_key_association ``` ```release-note:enhancement resource/aws_dx_connection: Add arguments to enable MACsec support +resource/aws_dx_connection: Add `skip_destroy` argument ``` \ No newline at end of file diff --git a/internal/service/directconnect/connection.go b/internal/service/directconnect/connection.go index 6acdaba9157..a2d5661060d 100644 --- a/internal/service/directconnect/connection.go +++ b/internal/service/directconnect/connection.go @@ -93,6 +93,11 @@ func ResourceConnection() *schema.Resource { Computed: true, ForceNew: true, }, + "skip_destroy": { + Type: schema.TypeBool, + Default: false, + Optional: true, + }, "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), "vlan_id": { @@ -226,6 +231,11 @@ func resourceConnectionUpdate(d *schema.ResourceData, meta interface{}) error { } func resourceConnectionDelete(d *schema.ResourceData, meta interface{}) error { + if v, ok := d.GetOk("skip_destroy"); ok && v.(bool) { + log.Printf("[DEBUG] Retaining Direct Connect Connection: %s", d.Id()) + return nil + } + conn := meta.(*conns.AWSClient).DirectConnectConn return deleteConnection(conn, d.Id(), waitConnectionDeleted) diff --git a/internal/service/directconnect/connection_test.go b/internal/service/directconnect/connection_test.go index 119b83c7a56..cc9010895b2 100644 --- a/internal/service/directconnect/connection_test.go +++ b/internal/service/directconnect/connection_test.go @@ -181,6 +181,28 @@ func TestAccDirectConnectConnection_providerName(t *testing.T) { }) } +func TestAccDirectConnectConnection_skipDestroy(t *testing.T) { + var connection directconnect.Connection + resourceName := "aws_dx_connection.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckConnectionNoDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConnectionConfig_skipDestroy(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConnectionExists(resourceName, &connection), + resource.TestCheckResourceAttr(resourceName, "skip_destroy", "true"), + ), + }, + }, + }) +} + func TestAccDirectConnectConnection_tags(t *testing.T) { var connection directconnect.Connection resourceName := "aws_dx_connection.test" @@ -279,6 +301,22 @@ func testAccCheckConnectionExists(name string, v *directconnect.Connection) reso } } +func testAccCheckConnectionNoDestroy(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_dx_connection" { + continue + } + + _, err := tfdirectconnect.FindConnectionByID(conn, rs.Primary.ID) + + return err + } + + return nil +} + func testAccConnectionConfig_basic(rName string) string { return fmt.Sprintf(` data "aws_dx_locations" "test" {} @@ -299,10 +337,11 @@ resource "aws_dx_connection" "test" { func testAccConnectionConfig_encryptionModeMustEncrypt(rName string) string { return fmt.Sprintf(` resource "aws_dx_connection" "test" { - name = %[1]q - location = "CSOW" - bandwidth = "100Gbps" + name = %[1]q + location = "CSOW" + bandwidth = "100Gbps" encryption_mode = "must_encrypt" + skip_destroy = true } `, rName) } @@ -310,11 +349,12 @@ resource "aws_dx_connection" "test" { func testAccConnectionConfig_encryptionMode(rName string) string { return fmt.Sprintf(` resource "aws_dx_connection" "test" { - name = %[1]q - location = "CSOW" - bandwidth = "100Gbps" + name = %[1]q + location = "CSOW" + bandwidth = "100Gbps" encryption_mode = "should_encrypt" - } + skip_destroy = true +} `, rName) } @@ -365,6 +405,24 @@ resource "aws_dx_connection" "test" { `, rName) } +func testAccConnectionConfig_skipDestroy(rName string) string { + return fmt.Sprintf(` +data "aws_dx_locations" "test" {} + +locals { + location_codes = tolist(data.aws_dx_locations.test.location_codes) + idx = min(2, length(local.location_codes) - 1) +} + +resource "aws_dx_connection" "test" { + name = %[1]q + bandwidth = "1Gbps" + location = local.location_codes[local.idx] + skip_destroy = true +} + `, rName) +} + func testAccConnectionConfig_tags1(rName, tagKey1, tagValue1 string) string { return fmt.Sprintf(` data "aws_dx_locations" "test" {} diff --git a/website/docs/r/dx_connection.html.markdown b/website/docs/r/dx_connection.html.markdown index d85cfd592ff..df6e423ce58 100644 --- a/website/docs/r/dx_connection.html.markdown +++ b/website/docs/r/dx_connection.html.markdown @@ -53,12 +53,13 @@ The following arguments are supported: * `bandwidth` - (Required) The bandwidth of the connection. Valid values for dedicated connections: 1Gbps, 10Gbps. Valid values for hosted connections: 50Mbps, 100Mbps, 200Mbps, 300Mbps, 400Mbps, 500Mbps, 1Gbps, 2Gbps, 5Gbps, 10Gbps and 100Gbps. Case sensitive. * `encryption_mode` - (Optional) The connection MAC Security (MACsec) encryption mode. MAC Security (MACsec) is only available on dedicated connections. Valid values are `no_encrypt`, `should_encrypt`, and `must_encrypt`. * `location` - (Required) The AWS Direct Connect location where the connection is located. See [DescribeLocations](https://docs.aws.amazon.com/directconnect/latest/APIReference/API_DescribeLocations.html) for the list of AWS Direct Connect locations. Use `locationCode`. +* `name` - (Required) The name of the connection. +* `provider_name` - (Optional) The name of the service provider associated with the connection. * `request_macsec` - (Optional) Boolean value indicating whether you want the connection to support MAC Security (MACsec). MAC Security (MACsec) is only available on dedicated connections. See [MACsec prerequisites](https://docs.aws.amazon.com/directconnect/latest/UserGuide/direct-connect-mac-sec-getting-started.html#mac-sec-prerequisites) for more information about MAC Security (MACsec) prerequisites. Default value: `false`. -~> **NOTE:** Changing the value of `macsec_requested` will cause the resource to be destroyed and re-created. +~> **NOTE:** Changing the value of `request_macsec` will cause the resource to be destroyed and re-created. -* `name` - (Required) The name of the connection. -* `provider_name` - (Optional) The name of the service provider associated with the connection. +* `skip_destroy` - (Optional) Set to true if you do not wish the connection to be deleted at destroy time, and instead just removed from the Terraform state. * `tags` - (Optional) A map of tags to assign to the resource. 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. ## Attributes Reference From 4f71c0674000189ec690f30449056d9386304444 Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Fri, 16 Dec 2022 14:58:10 -0500 Subject: [PATCH 23/30] WIP - encryption_mode acceptance test --- .../service/directconnect/connection_test.go | 53 +++++++++++++------ 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/internal/service/directconnect/connection_test.go b/internal/service/directconnect/connection_test.go index cc9010895b2..9fb119dddc0 100644 --- a/internal/service/directconnect/connection_test.go +++ b/internal/service/directconnect/connection_test.go @@ -43,9 +43,10 @@ func TestAccDirectConnectConnection_basic(t *testing.T) { }, // Test import. { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"request_macsec", "skip_destroy"}, }, }, }) @@ -84,15 +85,17 @@ func TestAccDirectConnectConnection_encryptionMode(t *testing.T) { dxName := "DX_CONNECTION_NAME" connectionName := os.Getenv(dxName) if connectionName == "" { - t.Skipf("Environment variable %s is not set", connectionName) + t.Skipf("Environment variable %s is not set", dxName) } + var connection directconnect.Connection resourceName := "aws_dx_connection.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccConnectionConfig_encryptionMode(connectionName), @@ -102,9 +105,14 @@ func TestAccDirectConnectConnection_encryptionMode(t *testing.T) { ImportStatePersist: true, }, { - Config: testAccConnectionConfig_encryptionModeMustEncrypt(resourceName), + Config: testAccConnectionConfig_encryptionModeMustEncrypt(connectionName), Check: resource.ComposeTestCheckFunc( + testAccCheckConnectionExists(resourceName, &connection), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(connectionId)), resource.TestCheckResourceAttr(resourceName, "encryption_mode", "must_encrypt"), + resource.TestCheckResourceAttrSet(resourceName, "location"), + resource.TestCheckResourceAttr(resourceName, "name", connectionName), + resource.TestCheckResourceAttr(resourceName, "skip_destroy", "true"), ), }, }, @@ -137,11 +145,10 @@ func TestAccDirectConnectConnection_macsecRequested(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - // Ignore the "macsec_requested" attribute as isn't returned by the API during read/refresh - ImportStateVerifyIgnore: []string{"request_macsec"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"request_macsec", "skip_destroy"}, }, }, }) @@ -173,9 +180,10 @@ func TestAccDirectConnectConnection_providerName(t *testing.T) { }, // Test import. { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"request_macsec", "skip_destroy"}, }, }, }) @@ -225,9 +233,10 @@ func TestAccDirectConnectConnection_tags(t *testing.T) { }, // Test import. { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"request_macsec", "skip_destroy"}, }, { Config: testAccConnectionConfig_tags2(rName, "key1", "value1updated", "key2", "value2"), @@ -252,6 +261,18 @@ func TestAccDirectConnectConnection_tags(t *testing.T) { }) } +// func testAccComposeImportStateCheck(fs ...resource.ImportStateCheckFunc) resource.ImportStateCheckFunc { +// return func(s []*terraform.InstanceState) error { +// for i, f := range fs { +// if err := f(s); err != nil { +// return fmt.Errorf("check %d/%d error: %s", i+1, len(fs), err) +// } +// } + +// return nil +// } +// } + func testAccCheckConnectionDestroy(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn From 63282e544bba0d8aa967355fe66e4bb5aff7fc01 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 16 Dec 2022 16:08:52 -0500 Subject: [PATCH 24/30] Tweak CHANGELOG entries. --- .changelog/26274.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.changelog/26274.txt b/.changelog/26274.txt index 9145117b744..88a61a564ba 100644 --- a/.changelog/26274.txt +++ b/.changelog/26274.txt @@ -3,6 +3,9 @@ aws_dx_macsec_key_association ``` ```release-note:enhancement -resource/aws_dx_connection: Add arguments to enable MACsec support +resource/aws_dx_connection: Add `encryption_mode` and `request_macsec` arguments and `macsec_capable` and `port_encryption_status` attributes in support of [MACsec](https://docs.aws.amazon.com/directconnect/latest/UserGuide/MACsec.html) +``` + +```release-note:enhancement resource/aws_dx_connection: Add `skip_destroy` argument ``` \ No newline at end of file From 9da4e2b87ecaeac544e5d40225820052c7d35508 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 16 Dec 2022 16:10:40 -0500 Subject: [PATCH 25/30] Fix semgrep 'dgryski.semgrep-go.oddifsequence.odd-sequence-ifs'. --- internal/service/directconnect/macsec_key.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/service/directconnect/macsec_key.go b/internal/service/directconnect/macsec_key.go index 7a13c5bc81e..61c1293c70d 100644 --- a/internal/service/directconnect/macsec_key.go +++ b/internal/service/directconnect/macsec_key.go @@ -151,13 +151,11 @@ func MacSecKeyParseSecretARN(output *directconnect.AssociateMacSecKeyOutput) str var result string for _, key := range output.MacSecKeys { - if key == nil { - continue - } if key != nil { result = aws.StringValue(key.SecretARN) } } + return result } From 3d6318c1997b0c5acfc4672b2fdbfa1dd6c7f168 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 16 Dec 2022 16:12:15 -0500 Subject: [PATCH 26/30] Fix golangci-lint 'whitespace'. --- internal/service/directconnect/macsec_key_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/service/directconnect/macsec_key_test.go b/internal/service/directconnect/macsec_key_test.go index ed89ab52957..ca50f1df2cc 100644 --- a/internal/service/directconnect/macsec_key_test.go +++ b/internal/service/directconnect/macsec_key_test.go @@ -88,7 +88,6 @@ func TestAccDirectConnectMacSecKey_withSecret(t *testing.T) { // testAccDirecConnectMacSecGenerateKey generates a 64-character hex string to be used as CKN or CAK func testAccDirecConnectMacSecGenerateHex() string { - s := make([]byte, 32) if _, err := rand.Read(s); err != nil { return "" From e87145ee2714107a3a81038df4223cd6195d24e6 Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Fri, 16 Dec 2022 16:17:08 -0500 Subject: [PATCH 27/30] Remove unused commented functions --- internal/service/directconnect/connection_test.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/internal/service/directconnect/connection_test.go b/internal/service/directconnect/connection_test.go index 9fb119dddc0..26b49abb503 100644 --- a/internal/service/directconnect/connection_test.go +++ b/internal/service/directconnect/connection_test.go @@ -261,18 +261,6 @@ func TestAccDirectConnectConnection_tags(t *testing.T) { }) } -// func testAccComposeImportStateCheck(fs ...resource.ImportStateCheckFunc) resource.ImportStateCheckFunc { -// return func(s []*terraform.InstanceState) error { -// for i, f := range fs { -// if err := f(s); err != nil { -// return fmt.Errorf("check %d/%d error: %s", i+1, len(fs), err) -// } -// } - -// return nil -// } -// } - func testAccCheckConnectionDestroy(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn From 3f6e762fda25e9e43e07881ca040bb1e75709581 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 16 Dec 2022 16:43:43 -0500 Subject: [PATCH 28/30] Cosmetics. --- internal/service/directconnect/connection.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/service/directconnect/connection.go b/internal/service/directconnect/connection.go index a2d5661060d..dd53464c536 100644 --- a/internal/service/directconnect/connection.go +++ b/internal/service/directconnect/connection.go @@ -139,7 +139,6 @@ func resourceConnectionCreate(d *schema.ResourceData, meta interface{}) error { } d.SetId(aws.StringValue(output.ConnectionId)) - d.Set("request_macsec", d.Get("request_macsec").(bool)) return resourceConnectionRead(d, meta) } @@ -218,9 +217,9 @@ func resourceConnectionUpdate(d *schema.ResourceData, meta interface{}) error { } } - arn := d.Get("arn").(string) if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") + arn := d.Get("arn").(string) if err := UpdateTags(conn, arn, o, n); err != nil { return fmt.Errorf("error updating Direct Connect Connection (%s) tags: %w", arn, err) From 9e087b5a2d335be7024e1146840de2a38d359322 Mon Sep 17 00:00:00 2001 From: Dave DeRicco <30156588+ddericco@users.noreply.github.com> Date: Sun, 18 Dec 2022 15:50:18 -0500 Subject: [PATCH 29/30] Finalize encryption_mode acc test --- internal/service/directconnect/connection.go | 10 ++++++ .../service/directconnect/connection_test.go | 35 ++++++++++++++----- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/internal/service/directconnect/connection.go b/internal/service/directconnect/connection.go index dd53464c536..e6890601369 100644 --- a/internal/service/directconnect/connection.go +++ b/internal/service/directconnect/connection.go @@ -181,6 +181,12 @@ func resourceConnectionRead(d *schema.ResourceData, meta interface{}) error { d.Set("provider_name", connection.ProviderName) d.Set("vlan_id", connection.Vlan) + // d.Set("request_macsec", d.Get("request_macsec").(bool)) + + if !d.IsNewResource() && !d.Get("request_macsec").(bool) { + d.Set("request_macsec", aws.Bool(false)) + } + tags, err := ListTags(conn, arn) if err != nil { @@ -215,6 +221,10 @@ func resourceConnectionUpdate(d *schema.ResourceData, meta interface{}) error { if err != nil { return fmt.Errorf("error modifying Direct Connect connection (%s) attributes: %s", d.Id(), err) } + + if _, err := waitConnectionConfirmed(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for Direct Connect connection (%s) to become available: %w", d.Id(), err) + } } if d.HasChange("tags_all") { diff --git a/internal/service/directconnect/connection_test.go b/internal/service/directconnect/connection_test.go index 26b49abb503..fa4c606ab23 100644 --- a/internal/service/directconnect/connection_test.go +++ b/internal/service/directconnect/connection_test.go @@ -90,6 +90,8 @@ func TestAccDirectConnectConnection_encryptionMode(t *testing.T) { var connection directconnect.Connection resourceName := "aws_dx_connection.test" + ckn := testAccDirecConnectMacSecGenerateHex() + cak := testAccDirecConnectMacSecGenerateHex() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -98,18 +100,29 @@ func TestAccDirectConnectConnection_encryptionMode(t *testing.T) { CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { - Config: testAccConnectionConfig_encryptionMode(connectionName), + Config: testAccConnectionConfig_encryptionModeShouldEncrypt(connectionName, ckn, cak), ResourceName: resourceName, ImportState: true, ImportStateId: connectionId, ImportStatePersist: true, }, { - Config: testAccConnectionConfig_encryptionModeMustEncrypt(connectionName), + Config: testAccConnectionConfig_encryptionModeNoEncrypt(connectionName), Check: resource.ComposeTestCheckFunc( testAccCheckConnectionExists(resourceName, &connection), - acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(connectionId)), - resource.TestCheckResourceAttr(resourceName, "encryption_mode", "must_encrypt"), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(`dxcon/.+`)), + resource.TestCheckResourceAttr(resourceName, "encryption_mode", "no_encrypt"), + resource.TestCheckResourceAttrSet(resourceName, "location"), + resource.TestCheckResourceAttr(resourceName, "name", connectionName), + resource.TestCheckResourceAttr(resourceName, "skip_destroy", "true"), + ), + }, + { + Config: testAccConnectionConfig_encryptionModeShouldEncrypt(connectionName, ckn, cak), + Check: resource.ComposeTestCheckFunc( + testAccCheckConnectionExists(resourceName, &connection), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(`dxcon/.+`)), + resource.TestCheckResourceAttr(resourceName, "encryption_mode", "should_encrypt"), resource.TestCheckResourceAttrSet(resourceName, "location"), resource.TestCheckResourceAttr(resourceName, "name", connectionName), resource.TestCheckResourceAttr(resourceName, "skip_destroy", "true"), @@ -343,19 +356,19 @@ resource "aws_dx_connection" "test" { `, rName) } -func testAccConnectionConfig_encryptionModeMustEncrypt(rName string) string { +func testAccConnectionConfig_encryptionModeNoEncrypt(rName string) string { return fmt.Sprintf(` resource "aws_dx_connection" "test" { name = %[1]q location = "CSOW" bandwidth = "100Gbps" - encryption_mode = "must_encrypt" + encryption_mode = "no_encrypt" skip_destroy = true } `, rName) } -func testAccConnectionConfig_encryptionMode(rName string) string { +func testAccConnectionConfig_encryptionModeShouldEncrypt(rName, ckn, cak string) string { return fmt.Sprintf(` resource "aws_dx_connection" "test" { name = %[1]q @@ -364,7 +377,13 @@ resource "aws_dx_connection" "test" { encryption_mode = "should_encrypt" skip_destroy = true } -`, rName) + +resource "aws_dx_macsec_key_association" "test" { + connection_id = aws_dx_connection.test.id + ckn = %[2]q + cak = %[3]q +} +`, rName, ckn, cak) } func testAccConnectionConfig_macsecEnabled(rName string) string { From 1a820f4cf1cfecbfa07d5b9380676eff93c6bff6 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 19 Dec 2022 08:16:50 -0500 Subject: [PATCH 30/30] r/aws_dx_connection: Fix golangci-lint 'unparam'. --- internal/service/directconnect/wait.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/directconnect/wait.go b/internal/service/directconnect/wait.go index f680f8f5526..f0f1e4d9320 100644 --- a/internal/service/directconnect/wait.go +++ b/internal/service/directconnect/wait.go @@ -18,7 +18,7 @@ const ( lagDeletedTimeout = 10 * time.Minute ) -func waitConnectionConfirmed(conn *directconnect.DirectConnect, id string) (*directconnect.Connection, error) { +func waitConnectionConfirmed(conn *directconnect.DirectConnect, id string) (*directconnect.Connection, error) { //nolint:unparam stateConf := &resource.StateChangeConf{ Pending: []string{directconnect.ConnectionStatePending, directconnect.ConnectionStateOrdering, directconnect.ConnectionStateRequested}, Target: []string{directconnect.ConnectionStateAvailable},