Skip to content

Commit

Permalink
r/aws_key_pair: Support importing 'key_name_prefix' (#9574).
Browse files Browse the repository at this point in the history
Acceptance test output:

% make testacc PKG_NAME=internal/service/ec2 TESTARGS='-run=TestAccEC2KeyPair_'
==> Checking that code complies with gofmt requirements...
TF_ACC=1 go test ./internal/service/ec2/... -v -count 1 -parallel 20 -run=TestAccEC2KeyPair_ -timeout 180m
=== RUN   TestAccEC2KeyPair_basic
=== PAUSE TestAccEC2KeyPair_basic
=== RUN   TestAccEC2KeyPair_tags
=== PAUSE TestAccEC2KeyPair_tags
=== RUN   TestAccEC2KeyPair_nameGenerated
=== PAUSE TestAccEC2KeyPair_nameGenerated
=== RUN   TestAccEC2KeyPair_namePrefix
=== PAUSE TestAccEC2KeyPair_namePrefix
=== RUN   TestAccEC2KeyPair_disappears
=== PAUSE TestAccEC2KeyPair_disappears
=== CONT  TestAccEC2KeyPair_basic
=== CONT  TestAccEC2KeyPair_namePrefix
=== CONT  TestAccEC2KeyPair_nameGenerated
=== CONT  TestAccEC2KeyPair_tags
=== CONT  TestAccEC2KeyPair_disappears
--- PASS: TestAccEC2KeyPair_disappears (12.31s)
--- PASS: TestAccEC2KeyPair_namePrefix (15.84s)
--- PASS: TestAccEC2KeyPair_basic (16.10s)
--- PASS: TestAccEC2KeyPair_nameGenerated (16.95s)
--- PASS: TestAccEC2KeyPair_tags (35.31s)
PASS
ok  	github.com/hashicorp/terraform-provider-aws/internal/service/ec2	41.854s
  • Loading branch information
ewbankkit committed Nov 10, 2021
1 parent e17687c commit ca339df
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 110 deletions.
117 changes: 52 additions & 65 deletions internal/service/ec2/key_pair.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/arn"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"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"
"github.com/hashicorp/terraform-provider-aws/internal/create"
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
)

Expand All @@ -34,20 +35,33 @@ func ResourceKeyPair() *schema.Resource {
MigrateState: KeyPairMigrateState,

Schema: map[string]*schema.Schema{
"arn": {
Type: schema.TypeString,
Computed: true,
},
"fingerprint": {
Type: schema.TypeString,
Computed: true,
},
"key_name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ConflictsWith: []string{"key_name_prefix"},
ValidateFunc: validation.StringLenBetween(0, 255),
ConflictsWith: []string{"key_name_prefix"},
},
"key_name_prefix": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ConflictsWith: []string{"key_name"},
ValidateFunc: validation.StringLenBetween(0, 255-resource.UniqueIDSuffixLength),
ConflictsWith: []string{"key_name"},
},
"key_pair_id": {
Type: schema.TypeString,
Computed: true,
},
"public_key": {
Type: schema.TypeString,
Expand All @@ -62,20 +76,8 @@ func ResourceKeyPair() *schema.Resource {
}
},
},
"fingerprint": {
Type: schema.TypeString,
Computed: true,
},
"key_pair_id": {
Type: schema.TypeString,
Computed: true,
},
"tags": tftags.TagsSchema(),
"tags_all": tftags.TagsSchemaComputed(),
"arn": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
Expand All @@ -85,29 +87,21 @@ func resourceKeyPairCreate(d *schema.ResourceData, meta interface{}) error {
defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig
tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{})))

var keyName string
if v, ok := d.GetOk("key_name"); ok {
keyName = v.(string)
} else if v, ok := d.GetOk("key_name_prefix"); ok {
keyName = resource.PrefixedUniqueId(v.(string))
d.Set("key_name", keyName)
} else {
keyName = resource.UniqueId()
d.Set("key_name", keyName)
}
keyName := create.Name(d.Get("key_name").(string), d.Get("key_name_prefix").(string))

publicKey := d.Get("public_key").(string)
req := &ec2.ImportKeyPairInput{
input := &ec2.ImportKeyPairInput{
KeyName: aws.String(keyName),
PublicKeyMaterial: []byte(publicKey),
PublicKeyMaterial: []byte(d.Get("public_key").(string)),
TagSpecifications: ec2TagSpecificationsFromKeyValueTags(tags, ec2.ResourceTypeKeyPair),
}
resp, err := conn.ImportKeyPair(req)

output, err := conn.ImportKeyPair(input)

if err != nil {
return fmt.Errorf("Error import KeyPair: %s", err)
return fmt.Errorf("error importing EC2 Key Pair (%s): %w", keyName, err)
}

d.SetId(aws.StringValue(resp.KeyName))
d.SetId(aws.StringValue(output.KeyName))

return resourceKeyPairRead(d, meta)
}
Expand All @@ -117,35 +111,32 @@ func resourceKeyPairRead(d *schema.ResourceData, meta interface{}) error {
defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig
ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig

req := &ec2.DescribeKeyPairsInput{
KeyNames: []*string{aws.String(d.Id())},
}
resp, err := conn.DescribeKeyPairs(req)
if err != nil {
if tfawserr.ErrMessageContains(err, "InvalidKeyPair.NotFound", "") {
log.Printf("[WARN] Key Pair (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}
return fmt.Errorf("Error retrieving KeyPair: %s", err)
}
keyPair, err := FindKeyPairByName(conn, d.Id())

if len(resp.KeyPairs) == 0 {
log.Printf("[WARN] Key Pair (%s) not found, removing from state", d.Id())
if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] EC2 Key Pair (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

kp := resp.KeyPairs[0]

if aws.StringValue(kp.KeyName) != d.Id() {
return fmt.Errorf("Unable to find key pair within: %#v", resp.KeyPairs)
if err != nil {
return fmt.Errorf("error reading EC2 Key Pair (%s): %w", d.Id(), err)
}

d.Set("key_name", kp.KeyName)
d.Set("fingerprint", kp.KeyFingerprint)
d.Set("key_pair_id", kp.KeyPairId)
tags := KeyValueTags(kp.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig)
arn := arn.ARN{
Partition: meta.(*conns.AWSClient).Partition,
Service: ec2.ServiceName,
Region: meta.(*conns.AWSClient).Region,
AccountID: meta.(*conns.AWSClient).AccountID,
Resource: fmt.Sprintf("key-pair/%s", d.Id()),
}.String()
d.Set("arn", arn)
d.Set("fingerprint", keyPair.KeyFingerprint)
d.Set("key_name", keyPair.KeyName)
d.Set("key_name_prefix", create.NamePrefixFromName(aws.StringValue(keyPair.KeyName)))
d.Set("key_pair_id", keyPair.KeyPairId)

tags := KeyValueTags(keyPair.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig)

//lintignore:AWSR002
if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil {
Expand All @@ -156,16 +147,6 @@ func resourceKeyPairRead(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("error setting tags_all: %w", err)
}

arn := arn.ARN{
Partition: meta.(*conns.AWSClient).Partition,
Service: ec2.ServiceName,
Region: meta.(*conns.AWSClient).Region,
AccountID: meta.(*conns.AWSClient).AccountID,
Resource: fmt.Sprintf("key-pair/%s", d.Id()),
}.String()

d.Set("arn", arn)

return nil
}

Expand All @@ -175,7 +156,7 @@ func resourceKeyPairUpdate(d *schema.ResourceData, meta interface{}) error {
if d.HasChange("tags_all") {
o, n := d.GetChange("tags_all")
if err := UpdateTags(conn, d.Get("key_pair_id").(string), o, n); err != nil {
return fmt.Errorf("error adding tags: %s", err)
return fmt.Errorf("error updating tags: %w", err)
}
}

Expand All @@ -185,8 +166,14 @@ func resourceKeyPairUpdate(d *schema.ResourceData, meta interface{}) error {
func resourceKeyPairDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).EC2Conn

log.Printf("[DEBUG] Deleting EC2 Key Pair: %s", d.Id())
_, err := conn.DeleteKeyPair(&ec2.DeleteKeyPairInput{
KeyName: aws.String(d.Id()),
})
return err

if err != nil {
return fmt.Errorf("error deleting EC2 Key Pair (%s): %w", d.Id(), err)
}

return nil
}
73 changes: 28 additions & 45 deletions internal/service/ec2/key_pair_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@ package ec2_test
import (
"fmt"
"regexp"
"strings"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/hashicorp/terraform-provider-aws/internal/acctest"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/create"
tfec2 "github.com/hashicorp/terraform-provider-aws/internal/service/ec2"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
)

func TestAccEC2KeyPair_basic(t *testing.T) {
Expand All @@ -40,6 +39,7 @@ func TestAccEC2KeyPair_basic(t *testing.T) {
acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "ec2", fmt.Sprintf("key-pair/%s", rName)),
resource.TestMatchResourceAttr(resourceName, "fingerprint", regexp.MustCompile(`[a-f0-9]{2}(:[a-f0-9]{2}){15}`)),
resource.TestCheckResourceAttr(resourceName, "key_name", rName),
resource.TestCheckResourceAttr(resourceName, "key_name_prefix", ""),
resource.TestCheckResourceAttr(resourceName, "public_key", publicKey),
),
},
Expand Down Expand Up @@ -104,7 +104,7 @@ func TestAccEC2KeyPair_tags(t *testing.T) {
})
}

func TestAccEC2KeyPair_generatedName(t *testing.T) {
func TestAccEC2KeyPair_nameGenerated(t *testing.T) {
var keyPair ec2.KeyPairInfo
resourceName := "aws_key_pair.test"

Expand All @@ -120,11 +120,11 @@ func TestAccEC2KeyPair_generatedName(t *testing.T) {
CheckDestroy: testAccCheckKeyPairDestroy,
Steps: []resource.TestStep{
{
Config: testAccKeyPairConfig_generatedName(publicKey),
Config: testAccKeyPairNameGeneratedConfig(publicKey),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKeyPairExists(resourceName, &keyPair),
testAccCheckKeyPairKeyNamePrefix(&keyPair, "terraform-"),
resource.TestMatchResourceAttr(resourceName, "key_name", regexp.MustCompile(`^terraform-`)),
create.TestCheckResourceAttrNameGenerated(resourceName, "key_name"),
resource.TestCheckResourceAttr(resourceName, "key_name_prefix", "terraform-"),
),
},
{
Expand Down Expand Up @@ -153,18 +153,18 @@ func TestAccEC2KeyPair_namePrefix(t *testing.T) {
CheckDestroy: testAccCheckKeyPairDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckKeyPairPrefixNameConfig(publicKey),
Config: testAccCheckKeyPairNamePrefixConfig("tf-acc-test-prefix-", publicKey),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKeyPairExists(resourceName, &keyPair),
testAccCheckKeyPairKeyNamePrefix(&keyPair, "baz-"),
resource.TestMatchResourceAttr(resourceName, "key_name", regexp.MustCompile(`^baz-`)),
create.TestCheckResourceAttrNameFromPrefix(resourceName, "key_name", "tf-acc-test-prefix-"),
resource.TestCheckResourceAttr(resourceName, "key_name_prefix", "tf-acc-test-prefix-"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"key_name_prefix", "public_key"},
ImportStateVerifyIgnore: []string{"public_key"},
},
},
})
Expand Down Expand Up @@ -206,59 +206,42 @@ func testAccCheckKeyPairDestroy(s *terraform.State) error {
continue
}

// Try to find key pair
resp, err := conn.DescribeKeyPairs(&ec2.DescribeKeyPairsInput{
KeyNames: []*string{aws.String(rs.Primary.ID)},
})
if err == nil {
if len(resp.KeyPairs) > 0 {
return fmt.Errorf("still exist.")
}
return nil
_, err := tfec2.FindKeyPairByName(conn, rs.Primary.ID)

if tfresource.NotFound(err) {
continue
}

if !tfawserr.ErrMessageContains(err, "InvalidKeyPair.NotFound", "") {
if err != nil {
return err
}

return fmt.Errorf("EC2 Key Pair %s still exists", rs.Primary.ID)
}

return nil
}

func testAccCheckKeyPairKeyNamePrefix(conf *ec2.KeyPairInfo, namePrefix string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if !strings.HasPrefix(aws.StringValue(conf.KeyName), namePrefix) {
return fmt.Errorf("incorrect key name. expected %s prefix, got %s", namePrefix, aws.StringValue(conf.KeyName))
}
return nil
}
}

func testAccCheckKeyPairExists(n string, res *ec2.KeyPairInfo) resource.TestCheckFunc {
func testAccCheckKeyPairExists(n string, v *ec2.KeyPairInfo) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No KeyPair name is set")
return fmt.Errorf("No EC2 Key Pair ID is set")
}

conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn

resp, err := conn.DescribeKeyPairs(&ec2.DescribeKeyPairsInput{
KeyNames: []*string{aws.String(rs.Primary.ID)},
})
output, err := tfec2.FindKeyPairByName(conn, rs.Primary.ID)

if err != nil {
return err
}
if len(resp.KeyPairs) != 1 ||
aws.StringValue(resp.KeyPairs[0].KeyName) != rs.Primary.ID {
return fmt.Errorf("KeyPair not found")
}

*res = *resp.KeyPairs[0]
*v = *output

return nil
}
Expand Down Expand Up @@ -300,19 +283,19 @@ resource "aws_key_pair" "test" {
`, rName, publicKey, tagKey1, tagValue1, tagKey2, tagValue2)
}

func testAccKeyPairConfig_generatedName(publicKey string) string {
func testAccKeyPairNameGeneratedConfig(publicKey string) string {
return fmt.Sprintf(`
resource "aws_key_pair" "test" {
public_key = %[1]q
}
`, publicKey)
}

func testAccCheckKeyPairPrefixNameConfig(publicKey string) string {
func testAccCheckKeyPairNamePrefixConfig(namePrefix, publicKey string) string {
return fmt.Sprintf(`
resource "aws_key_pair" "test" {
key_name_prefix = "baz-"
public_key = %[1]q
key_name_prefix = %[1]q
public_key = %[2]q
}
`, publicKey)
`, namePrefix, publicKey)
}

0 comments on commit ca339df

Please sign in to comment.