Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix aws_key_pair import re-creation bug #40075

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/40075.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
resource/aws_key_pair: Import of aws_key_pair includes key pair, fixes recreation.
```
28 changes: 5 additions & 23 deletions internal/service/ec2/ec2_key_pair.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package ec2

import (
"bytes"
"context"
"log"
"strings"
Expand All @@ -24,7 +23,6 @@ import (
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
"github.com/hashicorp/terraform-provider-aws/names"
"golang.org/x/crypto/ssh"
)

// @SDKResource("aws_key_pair", name="Key Pair")
Expand Down Expand Up @@ -81,9 +79,10 @@ func resourceKeyPair() *schema.Resource {
Computed: true,
},
names.AttrPublicKey: {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Type: schema.TypeString,
Required: true,
ForceNew: true,
DiffSuppressFunc: verify.SuppressEquivalentOpenSSHPublicKeyDiffs,
StateFunc: func(v interface{}) string {
switch v := v.(type) {
case string:
Expand Down Expand Up @@ -150,6 +149,7 @@ func resourceKeyPairRead(ctx context.Context, d *schema.ResourceData, meta inter
d.Set("key_name_prefix", create.NamePrefixFromName(aws.ToString(keyPair.KeyName)))
d.Set("key_pair_id", keyPair.KeyPairId)
d.Set("key_type", keyPair.KeyType)
d.Set(names.AttrPublicKey, keyPair.PublicKey)

setTagsOut(ctx, keyPair.Tags)

Expand Down Expand Up @@ -179,21 +179,3 @@ func resourceKeyPairDelete(ctx context.Context, d *schema.ResourceData, meta int

return diags
}

// OpenSSHPublicKeysEqual returns whether or not two OpenSSH public key format strings represent the same key.
// Any key comment is ignored when comparing values.
func openSSHPublicKeysEqual(v1, v2 string) bool {
key1, _, _, _, err := ssh.ParseAuthorizedKey([]byte(v1))

if err != nil {
return false
}

key2, _, _, _, err := ssh.ParseAuthorizedKey([]byte(v2))

if err != nil {
return false
}

return key1.Type() == key2.Type() && bytes.Equal(key1.Marshal(), key2.Marshal())
}
4 changes: 2 additions & 2 deletions internal/service/ec2/ec2_key_pair_data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-provider-aws/internal/acctest"
tfec2 "github.com/hashicorp/terraform-provider-aws/internal/service/ec2"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
"github.com/hashicorp/terraform-provider-aws/names"
)

Expand Down Expand Up @@ -82,7 +82,7 @@ func TestAccEC2KeyPairDataSource_includePublicKey(t *testing.T) {
resource.TestCheckResourceAttrPair(dataSource1Name, "key_name", resourceName, "key_name"),
resource.TestCheckResourceAttrPair(dataSource1Name, "key_pair_id", resourceName, "key_pair_id"),
resource.TestCheckResourceAttrWith(dataSource1Name, names.AttrPublicKey, func(v string) error {
if !tfec2.OpenSSHPublicKeysEqual(v, publicKey) {
if !verify.SuppressEquivalentOpenSSHPublicKeyDiffs("", v, publicKey, nil) {
return fmt.Errorf("Attribute 'public_key' expected %q, not equal to %q", publicKey, v)
}

Expand Down
58 changes: 41 additions & 17 deletions internal/service/ec2/ec2_key_pair_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/hashicorp/terraform-provider-aws/internal/conns"
tfec2 "github.com/hashicorp/terraform-provider-aws/internal/service/ec2"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
"github.com/hashicorp/terraform-provider-aws/names"
)

Expand Down Expand Up @@ -45,14 +46,19 @@ func TestAccEC2KeyPair_basic(t *testing.T) {
resource.TestMatchResourceAttr(resourceName, "fingerprint", regexache.MustCompile(`[0-9a-f]{2}(:[0-9a-f]{2}){15}`)),
resource.TestCheckResourceAttr(resourceName, "key_name", rName),
resource.TestCheckResourceAttr(resourceName, "key_name_prefix", ""),
resource.TestCheckResourceAttr(resourceName, names.AttrPublicKey, publicKey),
resource.TestCheckResourceAttrWith(resourceName, names.AttrPublicKey, func(v string) error {
if !verify.SuppressEquivalentOpenSSHPublicKeyDiffs("", v, publicKey, nil) {
return fmt.Errorf("Attribute 'public_key' expected %q, not equal to %q", publicKey, v)
}

return nil
}),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{names.AttrPublicKey},
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
Expand Down Expand Up @@ -81,13 +87,19 @@ func TestAccEC2KeyPair_tags(t *testing.T) {
testAccCheckKeyPairExists(ctx, resourceName, &keyPair),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, "1"),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1),
resource.TestCheckResourceAttrWith(resourceName, names.AttrPublicKey, func(v string) error {
if !verify.SuppressEquivalentOpenSSHPublicKeyDiffs("", v, publicKey, nil) {
return fmt.Errorf("Attribute 'public_key' expected %q, not equal to %q", publicKey, v)
}

return nil
}),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{names.AttrPublicKey},
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccKeyPairConfig_tags2(rName, publicKey, acctest.CtKey1, acctest.CtValue1Updated, acctest.CtKey2, acctest.CtValue2),
Expand Down Expand Up @@ -132,13 +144,19 @@ func TestAccEC2KeyPair_nameGenerated(t *testing.T) {
testAccCheckKeyPairExists(ctx, resourceName, &keyPair),
acctest.CheckResourceAttrNameGenerated(resourceName, "key_name"),
resource.TestCheckResourceAttr(resourceName, "key_name_prefix", "terraform-"),
resource.TestCheckResourceAttrWith(resourceName, names.AttrPublicKey, func(v string) error {
if !verify.SuppressEquivalentOpenSSHPublicKeyDiffs("", v, publicKey, nil) {
return fmt.Errorf("Attribute 'public_key' expected %q, not equal to %q", publicKey, v)
}

return nil
}),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{names.AttrPublicKey},
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
Expand Down Expand Up @@ -166,13 +184,19 @@ func TestAccEC2KeyPair_namePrefix(t *testing.T) {
testAccCheckKeyPairExists(ctx, resourceName, &keyPair),
acctest.CheckResourceAttrNameFromPrefix(resourceName, "key_name", "tf-acc-test-prefix-"),
resource.TestCheckResourceAttr(resourceName, "key_name_prefix", "tf-acc-test-prefix-"),
resource.TestCheckResourceAttrWith(resourceName, names.AttrPublicKey, func(v string) error {
if !verify.SuppressEquivalentOpenSSHPublicKeyDiffs("", v, publicKey, nil) {
return fmt.Errorf("Attribute 'public_key' expected %q, not equal to %q", publicKey, v)
}

return nil
}),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{names.AttrPublicKey},
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
Expand Down
1 change: 0 additions & 1 deletion internal/service/ec2/exports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,6 @@ var (
NewAttributeFilterList = newAttributeFilterList
NewCustomFilterList = newCustomFilterList
NewTagFilterList = newTagFilterList
OpenSSHPublicKeysEqual = openSSHPublicKeysEqual
ParseInstanceType = parseInstanceType
ProtocolForValue = protocolForValue
ProtocolStateFunc = protocolStateFunc
Expand Down
3 changes: 2 additions & 1 deletion internal/service/ec2/find.go
Original file line number Diff line number Diff line change
Expand Up @@ -5477,7 +5477,8 @@ func findKeyPairs(ctx context.Context, conn *ec2.Client, input *ec2.DescribeKeyP

func findKeyPairByName(ctx context.Context, conn *ec2.Client, name string) (*awstypes.KeyPairInfo, error) {
input := &ec2.DescribeKeyPairsInput{
KeyNames: []string{name},
KeyNames: []string{name},
IncludePublicKey: aws.Bool(true),
}

output, err := findKeyPair(ctx, conn, input)
Expand Down
28 changes: 28 additions & 0 deletions internal/verify/open_ssh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package verify

import (
"bytes"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"golang.org/x/crypto/ssh"
)

// SuppressEquivalentOpenSSHPublicKeyDiffs returns whether two OpenSSH public key format strings represent the same key.
// Any key comment is ignored when comparing values.
func SuppressEquivalentOpenSSHPublicKeyDiffs(k, old, new string, d *schema.ResourceData) bool {
oldKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(old))
if err != nil {
return false
}

newKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(new))

if err != nil {
return false
}

return oldKey.Type() == newKey.Type() && bytes.Equal(oldKey.Marshal(), newKey.Marshal())
}
2 changes: 0 additions & 2 deletions website/docs/r/key_pair.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,3 @@ Using `terraform import`, import Key Pairs using the `key_name`. For example:
```console
% terraform import aws_key_pair.deployer deployer-key
```

~> **NOTE:** The AWS API does not include the public key in the response, so `terraform apply` will attempt to replace the key pair. There is currently no supported workaround for this limitation.
Loading