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

Add support Private Link setting for AWS Tranfer for SFTP Service #8121

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
82 changes: 82 additions & 0 deletions aws/resource_aws_transfer_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package aws
import (
"fmt"
"log"
"regexp"
"time"

"github.com/aws/aws-sdk-go/aws"
Expand Down Expand Up @@ -34,6 +35,40 @@ func resourceAwsTransferServer() *schema.Resource {
Computed: true,
},

"endpoint_type": {
Type: schema.TypeString,
Optional: true,
Default: transfer.EndpointTypePublic,
ValidateFunc: validation.StringInSlice([]string{
transfer.EndpointTypePublic,
transfer.EndpointTypeVpcEndpoint,
}, false),
},

"endpoint_details": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"vpc_endpoint_id": {
Type: schema.TypeString,
Required: true,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This can be simplified with the below but certainly not worth holding up this pull request for:

ValidateFunc: validation.StringMatch(regexp.MustCompile(`^vpce-[0-9a-f]{17}$`), "must match VPC Endpoint identifier beginning with vpce-"),

value := v.(string)
validNamePattern := "^vpce-[0-9a-f]{17}$"
validName, nameMatchErr := regexp.MatchString(validNamePattern, value)
if !validName || nameMatchErr != nil {
errors = append(errors, fmt.Errorf(
"%q must match regex '%v'", k, validNamePattern))
}
return
},
},
},
},
},

"invocation_role": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -103,6 +138,14 @@ func resourceAwsTransferServerCreate(d *schema.ResourceData, meta interface{}) e
createOpts.LoggingRole = aws.String(attr.(string))
}

if attr, ok := d.GetOk("endpoint_type"); ok {
createOpts.EndpointType = aws.String(attr.(string))
}

if attr, ok := d.GetOk("endpoint_details"); ok {
createOpts.EndpointDetails = expandTransferServerEndpointDetails(attr.([]interface{}))
}

log.Printf("[DEBUG] Create Transfer Server Option: %#v", createOpts)

resp, err := conn.CreateServer(createOpts)
Expand Down Expand Up @@ -144,6 +187,8 @@ func resourceAwsTransferServerRead(d *schema.ResourceData, meta interface{}) err
d.Set("invocation_role", aws.StringValue(resp.Server.IdentityProviderDetails.InvocationRole))
d.Set("url", aws.StringValue(resp.Server.IdentityProviderDetails.Url))
}
d.Set("endpoint_type", resp.Server.EndpointType)
d.Set("endpoint_details", flattenTransferServerEndpointDetails(resp.Server.EndpointDetails))
d.Set("identity_provider_type", resp.Server.IdentityProviderType)
d.Set("logging_role", resp.Server.LoggingRole)

Expand Down Expand Up @@ -178,6 +223,20 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e
updateOpts.IdentityProviderDetails = identityProviderDetails
}

if d.HasChange("endpoint_type") {
updateFlag = true
if attr, ok := d.GetOk("endpoint_type"); ok {
updateOpts.EndpointType = aws.String(attr.(string))
}
}

if d.HasChange("endpoint_details") {
updateFlag = true
if attr, ok := d.GetOk("endpoint_details"); ok {
updateOpts.EndpointDetails = expandTransferServerEndpointDetails(attr.([]interface{}))
}
}

if updateFlag {
_, err := conn.UpdateServer(updateOpts)
if err != nil {
Expand Down Expand Up @@ -285,3 +344,26 @@ func deleteTransferUsers(conn *transfer.Transfer, serverID string, nextToken *st

return nil
}

func expandTransferServerEndpointDetails(l []interface{}) *transfer.EndpointDetails {
if len(l) == 0 || l[0] == nil {
return nil
}
e := l[0].(map[string]interface{})

return &transfer.EndpointDetails{
VpcEndpointId: aws.String(e["vpc_endpoint_id"].(string)),
}
}

func flattenTransferServerEndpointDetails(endpointDetails *transfer.EndpointDetails) []interface{} {
if endpointDetails == nil {
return []interface{}{}
}

e := map[string]interface{}{
"vpc_endpoint_id": aws.StringValue(endpointDetails.VpcEndpointId),
}

return []interface{}{e}
}
92 changes: 92 additions & 0 deletions aws/resource_aws_transfer_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,34 @@ func TestAccAWSTransferServer_forcedestroy(t *testing.T) {
})
}

func TestAccAWSTransferServer_vpcEndpointId(t *testing.T) {
var conf transfer.DescribedServer
resourceName := "aws_transfer_server.default"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: resourceName,
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSTransferServerDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSTransferServerConfig_VpcEndPoint,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSTransferServerExists(resourceName, &conf),
resource.TestCheckResourceAttr(
resourceName, "endpoint_type", "VPC_ENDPOINT"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"force_destroy"},
},
},
})
}

func testAccCheckAWSTransferServerExists(n string, res *transfer.DescribedServer) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
Expand Down Expand Up @@ -467,3 +495,67 @@ POLICY
}
`, rName, rName)
}

const testAccAWSTransferServerConfig_VpcEndPoint = `

data "aws_region" "current" {}

resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"

tags = {
Name = "terraform-testacc-default-route-table-vpc-endpoint"
}
}

resource "aws_internet_gateway" "igw" {
vpc_id = "${aws_vpc.test.id}"

tags = {
Name = "test"
}
}

resource "aws_security_group" "sg" {
name = "allow-transfer-server"
description = "Allow TLS inbound traffic"
vpc_id = "${aws_vpc.test.id}"
ingress {
from_port = 0
to_port = 0
protocol = "-1"
self = true
}
}

resource "aws_vpc_endpoint" "transfer" {
vpc_id = "${aws_vpc.test.id}"
vpc_endpoint_type = "Interface"
service_name = "com.amazonaws.${data.aws_region.current.name}.transfer.server"

security_group_ids = [
"${aws_security_group.sg.id}",
]
}

resource "aws_default_route_table" "foo" {
default_route_table_id = "${aws_vpc.test.default_route_table_id}"

tags = {
Name = "test"
}

route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.igw.id}"
}
}


resource "aws_transfer_server" "default" {
endpoint_type = "VPC_ENDPOINT"
endpoint_details {
vpc_endpoint_id = "${aws_vpc_endpoint.transfer.id}"
}
}
`
6 changes: 6 additions & 0 deletions website/docs/r/transfer_server.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,19 @@ resource "aws_transfer_server" "foo" {

The following arguments are supported:

* `endpoint_details` - (Optional) The virtual private cloud (VPC) endpoint settings that you want to configure for your SFTP server. Fields documented below.
* `endpoint_type` - (Optional) The type of endpoint that you want your SFTP server connect to. If you connect to a `VPC_ENDPOINT`, your SFTP server isn't accessible over the public internet. If you want to connect your SFTP server via public internet, set `PUBLIC`.
* `invocation_role` - (Optional) Amazon Resource Name (ARN) of the IAM role used to authenticate the user account with an `identity_provider_type` of `API_GATEWAY`.
* `url` - (Optional) - URL of the service endpoint used to authenticate users with an `identity_provider_type` of `API_GATEWAY`.
* `identity_provider_type` - (Optional) The mode of authentication enabled for this service. The default value is `SERVICE_MANAGED`, which allows you to store and access SFTP user credentials within the service. `API_GATEWAY` indicates that user authentication requires a call to an API Gateway endpoint URL provided by you to integrate an identity provider of your choice.
* `logging_role` - (Optional) Amazon Resource Name (ARN) of an IAM role that allows the service to write your SFTP users’ activity to your Amazon CloudWatch logs for monitoring and auditing purposes.
* `force_destroy` - (Optional) A boolean that indicates all users associated with the server should be deleted so that the Server can be destroyed without error. The default value is `false`.
* `tags` - (Optional) A mapping of tags to assign to the resource.

**endpoint_details** requires the following:

* `vpc_endpoint_id` - (Required) The ID of the VPC endpoint.

## Attributes Reference
In addition to all arguments above, the following attributes are exported:

Expand Down