-
Notifications
You must be signed in to change notification settings - Fork 9.3k
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
Add Transfer SSH Public Key Resource Support #6932
Merged
bflad
merged 1 commit into
hashicorp:master
from
teraken0509:feature/add-support-for-sftp-ssh-key
Dec 20, 2018
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"strings" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/transfer" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
func resourceAwsTransferSshKey() *schema.Resource { | ||
|
||
return &schema.Resource{ | ||
Create: resourceAwsTransferSshKeyCreate, | ||
Read: resourceAwsTransferSshKeyRead, | ||
Delete: resourceAwsTransferSshKeyDelete, | ||
Importer: &schema.ResourceImporter{ | ||
State: schema.ImportStatePassthrough, | ||
}, | ||
Schema: map[string]*schema.Schema{ | ||
"body": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { | ||
old = cleanSshKey(old) | ||
new = cleanSshKey(new) | ||
return strings.Trim(old, "\n") == strings.Trim(new, "\n") | ||
}, | ||
}, | ||
|
||
"server_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validateTransferServerID, | ||
}, | ||
|
||
"user_name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validateTransferUserName, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceAwsTransferSshKeyCreate(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).transferconn | ||
userName := d.Get("user_name").(string) | ||
serverID := d.Get("server_id").(string) | ||
|
||
createOpts := &transfer.ImportSshPublicKeyInput{ | ||
ServerId: aws.String(serverID), | ||
UserName: aws.String(userName), | ||
SshPublicKeyBody: aws.String(d.Get("body").(string)), | ||
} | ||
|
||
log.Printf("[DEBUG] Create Transfer SSH Public Key Option: %#v", createOpts) | ||
|
||
resp, err := conn.ImportSshPublicKey(createOpts) | ||
if err != nil { | ||
return fmt.Errorf("Error importing ssh public key: %s", err) | ||
} | ||
|
||
d.SetId(fmt.Sprintf("%s/%s/%s", serverID, userName, *resp.SshPublicKeyId)) | ||
|
||
return nil | ||
} | ||
|
||
func resourceAwsTransferSshKeyRead(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).transferconn | ||
serverID, userName, sshKeyID, err := decodeTransferSshKeyId(d.Id()) | ||
if err != nil { | ||
return fmt.Errorf("error parsing Transfer SSH Public Key ID: %s", err) | ||
} | ||
|
||
descOpts := &transfer.DescribeUserInput{ | ||
UserName: aws.String(userName), | ||
ServerId: aws.String(serverID), | ||
} | ||
|
||
log.Printf("[DEBUG] Describe Transfer User Option: %#v", descOpts) | ||
|
||
resp, err := conn.DescribeUser(descOpts) | ||
if err != nil { | ||
if isAWSErr(err, transfer.ErrCodeResourceNotFoundException, "") { | ||
log.Printf("[WARN] Transfer User (%s) for Server (%s) not found, removing ssh public key (%s) from state", userName, serverID, sshKeyID) | ||
d.SetId("") | ||
return nil | ||
} | ||
return err | ||
} | ||
|
||
var body string | ||
for _, s := range resp.User.SshPublicKeys { | ||
if sshKeyID == *s.SshPublicKeyId { | ||
body = *s.SshPublicKeyBody | ||
} | ||
} | ||
|
||
if body == "" { | ||
log.Printf("[WARN] No such ssh public key found for User (%s) in Server (%s)", userName, serverID) | ||
d.SetId("") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Its safe in this case to be omitted, but generally we should immediately call |
||
} | ||
|
||
d.Set("server_id", resp.ServerId) | ||
d.Set("user_name", resp.User.UserName) | ||
d.Set("body", body) | ||
|
||
return nil | ||
} | ||
|
||
func resourceAwsTransferSshKeyDelete(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).transferconn | ||
serverID, userName, sshKeyID, err := decodeTransferSshKeyId(d.Id()) | ||
if err != nil { | ||
return fmt.Errorf("error parsing Transfer SSH Public Key ID: %s", err) | ||
} | ||
|
||
delOpts := &transfer.DeleteSshPublicKeyInput{ | ||
UserName: aws.String(userName), | ||
ServerId: aws.String(serverID), | ||
SshPublicKeyId: aws.String(sshKeyID), | ||
} | ||
|
||
log.Printf("[DEBUG] Delete Transfer SSH Public Key Option: %#v", delOpts) | ||
|
||
_, err = conn.DeleteSshPublicKey(delOpts) | ||
if err != nil { | ||
if isAWSErr(err, transfer.ErrCodeResourceNotFoundException, "") { | ||
return nil | ||
} | ||
return fmt.Errorf("error deleting Transfer User Ssh Key (%s): %s", d.Id(), err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func decodeTransferSshKeyId(id string) (string, string, string, error) { | ||
idParts := strings.SplitN(id, "/", 3) | ||
if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" { | ||
return "", "", "", fmt.Errorf("unexpected format of ID (%s), expected SERVERID/USERNAME/SSHKEYID", id) | ||
} | ||
return idParts[0], idParts[1], idParts[2], nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/transfer" | ||
|
||
"github.com/hashicorp/terraform/helper/acctest" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
func TestAccAWSTransferSshKey_basic(t *testing.T) { | ||
var conf transfer.SshPublicKey | ||
rName := acctest.RandString(5) | ||
|
||
resource.ParallelTest(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckAWSTransferSshKeyDestroy, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccAWSTransferSshKeyConfig_basic(rName), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckAWSTransferSshKeyExists("aws_transfer_ssh_key.foo", &conf), | ||
resource.TestCheckResourceAttrPair( | ||
"aws_transfer_ssh_key.foo", "server_id", "aws_transfer_server.foo", "id"), | ||
resource.TestCheckResourceAttrPair( | ||
"aws_transfer_ssh_key.foo", "user_name", "aws_transfer_user.foo", "user_name"), | ||
), | ||
}, | ||
{ | ||
ResourceName: "aws_transfer_user.foo", | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckAWSTransferSshKeyExists(n string, res *transfer.SshPublicKey) 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 Transfer Ssh Public Key ID is set") | ||
} | ||
|
||
conn := testAccProvider.Meta().(*AWSClient).transferconn | ||
serverID, userName, sshKeyID, err := decodeTransferSshKeyId(rs.Primary.ID) | ||
if err != nil { | ||
return fmt.Errorf("error parsing Transfer SSH Public Key ID: %s", err) | ||
} | ||
|
||
describe, err := conn.DescribeUser(&transfer.DescribeUserInput{ | ||
ServerId: aws.String(serverID), | ||
UserName: aws.String(userName), | ||
}) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, sshPublicKey := range describe.User.SshPublicKeys { | ||
if sshKeyID == *sshPublicKey.SshPublicKeyId { | ||
*res = *sshPublicKey | ||
return nil | ||
} | ||
} | ||
|
||
return fmt.Errorf("Transfer Ssh Public Key doesn't exists.") | ||
} | ||
} | ||
|
||
func testAccCheckAWSTransferSshKeyDestroy(s *terraform.State) error { | ||
conn := testAccProvider.Meta().(*AWSClient).transferconn | ||
|
||
for _, rs := range s.RootModule().Resources { | ||
if rs.Type != "aws_transfer_ssh_key" { | ||
continue | ||
} | ||
serverID, userName, sshKeyID, err := decodeTransferSshKeyId(rs.Primary.ID) | ||
if err != nil { | ||
return fmt.Errorf("error parsing Transfer SSH Public Key ID: %s", err) | ||
} | ||
|
||
describe, err := conn.DescribeUser(&transfer.DescribeUserInput{ | ||
UserName: aws.String(userName), | ||
ServerId: aws.String(serverID), | ||
}) | ||
|
||
if isAWSErr(err, transfer.ErrCodeResourceNotFoundException, "") { | ||
continue | ||
} | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, sshPublicKey := range describe.User.SshPublicKeys { | ||
if sshKeyID == *sshPublicKey.SshPublicKeyId { | ||
return fmt.Errorf("Transfer SSH Public Key still exists") | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func testAccAWSTransferSshKeyConfig_basic(rName string) string { | ||
return fmt.Sprintf(` | ||
resource "aws_transfer_server" "foo" { | ||
identity_provider_type = "SERVICE_MANAGED" | ||
} | ||
|
||
|
||
resource "aws_iam_role" "foo" { | ||
name = "tf-test-transfer-user-iam-role-%s" | ||
|
||
assume_role_policy = <<EOF | ||
{ | ||
"Version": "2012-10-17", | ||
"Statement": [ | ||
{ | ||
"Effect": "Allow", | ||
"Principal": { | ||
"Service": "transfer.amazonaws.com" | ||
}, | ||
"Action": "sts:AssumeRole" | ||
} | ||
] | ||
} | ||
EOF | ||
} | ||
|
||
resource "aws_iam_role_policy" "foo" { | ||
name = "tf-test-transfer-user-iam-policy-%s" | ||
role = "${aws_iam_role.foo.id}" | ||
policy = <<POLICY | ||
{ | ||
"Version": "2012-10-17", | ||
"Statement": [ | ||
{ | ||
"Sid": "AllowFullAccesstoS3", | ||
"Effect": "Allow", | ||
"Action": [ | ||
"s3:*" | ||
], | ||
"Resource": "*" | ||
} | ||
] | ||
} | ||
POLICY | ||
} | ||
|
||
|
||
resource "aws_transfer_user" "foo" { | ||
server_id = "${aws_transfer_server.foo.id}" | ||
user_name = "tftestuser" | ||
role = "${aws_iam_role.foo.arn}" | ||
} | ||
|
||
|
||
resource "aws_transfer_ssh_key" "foo" { | ||
server_id = "${aws_transfer_server.foo.id}" | ||
user_name = "${aws_transfer_user.foo.user_name}" | ||
body = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 phodgson@thoughtworks.com" | ||
} | ||
`, rName, rName) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: To prevent potential panics, we prefer to dereference API response values using the AWS Go SDK functions, e.g.
if sshKeyID == aws.StringValue(s.SshPublicKeyId)