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

F aws transfer connector #33415

Merged
merged 8 commits into from
Sep 13, 2023
Merged
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/32741.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_transfer_connector: Add `sftp_config` argument and make `as2_config` optional
```
111 changes: 79 additions & 32 deletions internal/service/transfer/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
"github.com/hashicorp/terraform-provider-aws/internal/flex"
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 Down Expand Up @@ -45,7 +46,7 @@ func ResourceConnector() *schema.Resource {
},
"as2_config": {
Type: schema.TypeList,
Required: true,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
Expand Down Expand Up @@ -97,6 +98,30 @@ func ResourceConnector() *schema.Resource {
Type: schema.TypeString,
Optional: true,
},
"sftp_config": {
Type: schema.TypeList,
MaxItems: 1,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"trusted_host_keys": {
Type: schema.TypeSet,
Optional: true,
MinItems: 1,
MaxItems: 10,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringLenBetween(1, 2028),
},
},
"user_secret_id": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringLenBetween(1, 2028),
},
},
},
},
names.AttrTags: tftags.TagsSchema(),
names.AttrTagsAll: tftags.TagsSchemaComputed(),
"url": {
Expand All @@ -115,15 +140,22 @@ func resourceConnectorCreate(ctx context.Context, d *schema.ResourceData, meta i

input := &transfer.CreateConnectorInput{
AccessRole: aws.String(d.Get("access_role").(string)),
As2Config: expandAs2Config(d.Get("as2_config").([]interface{})[0].(map[string]interface{})),
Tags: getTagsIn(ctx),
Url: aws.String(d.Get("url").(string)),
}

if v, ok := d.GetOk("as2_config"); ok {
input.As2Config = expandAs2Config(v.([]interface{}))
}

if v, ok := d.GetOk("logging_role"); ok {
input.LoggingRole = aws.String(v.(string))
}

if v, ok := d.GetOk("sftp_config"); ok {
input.SftpConfig = expandSftpConfig(v.([]interface{}))
}

output, err := conn.CreateConnectorWithContext(ctx, input)

if err != nil {
Expand Down Expand Up @@ -158,6 +190,9 @@ func resourceConnectorRead(ctx context.Context, d *schema.ResourceData, meta int
}
d.Set("connector_id", output.ConnectorId)
d.Set("logging_role", output.LoggingRole)
if err := d.Set("sftp_config", flattenSftpConfig(output.SftpConfig)); err != nil {
return sdkdiag.AppendErrorf(diags, "setting sftp_config: %s", err)
}
d.Set("url", output.Url)
setTagsOut(ctx, output.Tags)

Expand All @@ -178,15 +213,17 @@ func resourceConnectorUpdate(ctx context.Context, d *schema.ResourceData, meta i
}

if d.HasChange("as2_config") {
if v, ok := d.GetOk("as2_config"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
input.As2Config = expandAs2Config(v.([]interface{})[0].(map[string]interface{}))
}
input.As2Config = expandAs2Config(d.Get("as2_config").([]interface{}))
}

if d.HasChange("logging_role") {
input.LoggingRole = aws.String(d.Get("logging_role").(string))
}

if d.HasChange("sftp_config") {
input.SftpConfig = expandSftpConfig(d.Get("sftp_config").([]interface{}))
}

if d.HasChange("url") {
input.Url = aws.String(d.Get("url").(string))
}
Expand Down Expand Up @@ -221,46 +258,43 @@ func resourceConnectorDelete(ctx context.Context, d *schema.ResourceData, meta i
return diags
}

func expandAs2Config(tfMap map[string]interface{}) *transfer.As2ConnectorConfig {
if tfMap == nil {
func expandAs2Config(pUser []interface{}) *transfer.As2ConnectorConfig {
if len(pUser) < 1 || pUser[0] == nil {
return nil
}

apiObject := &transfer.As2ConnectorConfig{}

if v, ok := tfMap["compression"].(string); ok && v != "" {
apiObject.Compression = aws.String(v)
m := pUser[0].(map[string]interface{})

as2Config := &transfer.As2ConnectorConfig{
Compression: aws.String(m["compression"].(string)),
EncryptionAlgorithm: aws.String(m["encryption_algorithm"].(string)),
LocalProfileId: aws.String(m["local_profile_id"].(string)),
MdnResponse: aws.String(m["mdn_response"].(string)),
MdnSigningAlgorithm: aws.String(m["mdn_signing_algorithm"].(string)),
MessageSubject: aws.String(m["message_subject"].(string)),
PartnerProfileId: aws.String(m["partner_profile_id"].(string)),
SigningAlgorithm: aws.String(m["signing_algorithm"].(string)),
}

if v, ok := tfMap["encryption_algorithm"].(string); ok && v != "" {
apiObject.EncryptionAlgorithm = aws.String(v)
}

if v, ok := tfMap["local_profile_id"].(string); ok && v != "" {
apiObject.LocalProfileId = aws.String(v)
}

if v, ok := tfMap["mdn_response"].(string); ok && v != "" {
apiObject.MdnResponse = aws.String(v)
}
return as2Config
}

if v, ok := tfMap["mdn_signing_algorithm"].(string); ok && v != "" {
apiObject.MdnSigningAlgorithm = aws.String(v)
func expandSftpConfig(pUser []interface{}) *transfer.SftpConnectorConfig {
if len(pUser) < 1 || pUser[0] == nil {
return nil
}

if v, ok := tfMap["message_subject"].(string); ok && v != "" {
apiObject.MessageSubject = aws.String(v)
}
m := pUser[0].(map[string]interface{})

if v, ok := tfMap["partner_profile_id"].(string); ok && v != "" {
apiObject.PartnerProfileId = aws.String(v)
sftpConfig := &transfer.SftpConnectorConfig{
UserSecretId: aws.String(m["user_secret_id"].(string)),
}

if v, ok := tfMap["signing_algorithm"].(string); ok && v != "" {
apiObject.SigningAlgorithm = aws.String(v)
if v, ok := m["trusted_host_keys"].(*schema.Set); ok && len(v.List()) > 0 {
sftpConfig.TrustedHostKeys = flex.ExpandStringSet(v)
}

return apiObject
return sftpConfig
}

func flattenAs2Config(apiObject *transfer.As2ConnectorConfig) []interface{} {
Expand Down Expand Up @@ -304,3 +338,16 @@ func flattenAs2Config(apiObject *transfer.As2ConnectorConfig) []interface{} {

return []interface{}{tfMap}
}

func flattenSftpConfig(posixUser *transfer.SftpConnectorConfig) []interface{} {
if posixUser == nil {
return []interface{}{}
}

m := map[string]interface{}{
"trusted_host_keys": aws.StringValueSlice(posixUser.TrustedHostKeys),
"user_secret_id": aws.StringValue(posixUser.UserSecretId),
}

return []interface{}{m}
}
54 changes: 54 additions & 0 deletions internal/service/transfer/connector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,41 @@ func TestAccTransferConnector_basic(t *testing.T) {
})
}

func TestAccTransferConnector_sftpConfig(t *testing.T) {
ctx := acctest.Context(t)
var conf transfer.DescribedConnector
resourceName := "aws_transfer_connector.test"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
publicKey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNt3kA/dBkS6ZyU/sVDiGMuWJQaRPmLNbs/25K/e/fIl07ZWUgqqsFkcycLLMNFGD30Cmgp6XCXfNlIjzFWhNam+4cBb4DPpvieUw44VgsHK5JQy3JKlUfglmH5rs4G5pLiVfZpFU6jqvTsu4mE1CHCP0sXJlJhGxMG3QbsqYWNKiqGFEhuzGMs6fQlMkNiXsFoDmh33HAcXCbaFSC7V7xIqT1hlKu0iOL+GNjMj4R3xy0o3jafhO4MG2s3TwCQQCyaa5oyjL8iP8p3L9yp6cbIcXaS72SIgbCSGCyrcQPIKP2lJJHvE1oVWzLVBhR4eSzrlFDv7K4IErzaJmHqdiz" // nosemgrep:ci.ssh-key

resource.Test(t, resource.TestCase{
PreCheck: func() {
acctest.PreCheck(ctx, t)
acctest.PreCheckPartitionHasService(t, transfer.EndpointsID)
testAccPreCheck(ctx, t)
},
ErrorCheck: acctest.ErrorCheck(t, transfer.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckConnectorDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccConnectorConfig_sftpConfig(rName, "sftp://s-fakeserver.server.transfer.test.amazonaws.com", publicKey),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckConnectorExists(ctx, resourceName, &conf),
resource.TestCheckResourceAttrSet(resourceName, "arn"),
resource.TestCheckResourceAttr(resourceName, "tags.%", "0"),
resource.TestCheckResourceAttr(resourceName, "url", "sftp://s-fakeserver.server.transfer.test.amazonaws.com"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccTransferConnector_disappears(t *testing.T) {
ctx := acctest.Context(t)
var conf transfer.DescribedConnector
Expand Down Expand Up @@ -261,6 +296,25 @@ resource "aws_transfer_connector" "test" {
`, rName, url))
}

func testAccConnectorConfig_sftpConfig(rName, url, publickey string) string {
return acctest.ConfigCompose(testAccConnectorConfig_base(rName), fmt.Sprintf(`
resource "aws_transfer_connector" "test" {
access_role = aws_iam_role.test.arn

sftp_config {
trusted_host_keys = [%[3]q]
user_secret_id = aws_secretsmanager_secret.test.id
}

url = %[2]q
}

resource "aws_secretsmanager_secret" "test" {
name = %[1]q
}
`, rName, url, publickey))
}

func testAccConnectorConfig_tags1(rName, url, tagKey1, tagValue1 string) string {
return acctest.ConfigCompose(testAccConnectorConfig_base(rName), fmt.Sprintf(`
resource "aws_transfer_connector" "test" {
Expand Down
25 changes: 22 additions & 3 deletions website/docs/r/transfer_connector.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,28 @@ resource "aws_transfer_connector" "example" {
}
```

### SFTP Connector

```terraform
resource "aws_transfer_connector" "example" {
access_role = aws_iam_role.test.arn
sftp_config {
trusted_host_keys = ["ssh-rsa AAAAB3NYourKeysHere"]
user_secret_id = aws_secretsmanager_secret.example.id
}
url = "sftp://test.com"
}
```

## Argument Reference

This resource supports the following arguments:

* `access_role` - (Required) The IAM Role which provides read and write access to the parent directory of the file location mentioned in the StartFileTransfer request.
* `as2_config` - (Required) The parameters to configure for the connector object. Fields documented below.
* `as2_config` - (Optional) Either SFTP or AS2 is configured.The parameters to configure for the connector object. Fields documented below.
* `logging_role` - (Optional) The IAM Role which is required for allowing the connector to turn on CloudWatch logging for Amazon S3 events.
* `url` - (Required) The URL of the partners AS2 endpoint.
* `sftp_config` - (Optional) Either SFTP or AS2 is configured.The parameters to configure for the connector object. Fields documented below.
* `url` - (Required) The URL of the partners AS2 endpoint or SFTP endpoint.
* `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.

### As2Config Details
Expand All @@ -52,12 +66,17 @@ This resource supports the following arguments:
* `partner_profile_id` - (Required) The unique identifier for the AS2 partner profile.
* `signing_algorithm` - (Required) The algorithm that is used to sign AS2 messages sent with the connector. The valid values are SHA256 | SHA384 | SHA512 | SHA1 | NONE .

### SftpConfig Details

* `trusted_host_keys` - (Required) A list of public portion of the host key, or keys, that are used to authenticate the user to the external server to which you are connecting.(https://docs.aws.amazon.com/transfer/latest/userguide/API_SftpConnectorConfig.html)
* `user_secret_id` - (Required) The identifier for the secret (in AWS Secrets Manager) that contains the SFTP user's private key, password, or both. The identifier can be either the Amazon Resource Name (ARN) or the name of the secret.

## Attribute Reference

This resource exports the following attributes in addition to the arguments above:

* `arn` - The ARN of the connector.
* `connector_id` - The unique identifier for the AS2 profile.
* `connector_id` - The unique identifier for the AS2 profile or SFTP Profile.

## Import

Expand Down
Loading