Skip to content

Commit

Permalink
r/transfer: add lambda identity provider support
Browse files Browse the repository at this point in the history
  • Loading branch information
nij4t committed Dec 3, 2021
1 parent 3540d70 commit 4aa2a2d
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 24 deletions.
95 changes: 71 additions & 24 deletions internal/service/transfer/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,17 @@ func ResourceServer() *schema.Resource {

CustomizeDiff: customdiff.Sequence(
verify.SetTagsDiff,
customdiff.ForceNewIfChange("endpoint_details.0.vpc_id", func(_ context.Context, old, new, meta interface{}) bool {
// "InvalidRequestException: Changing VpcId is not supported".
if old, new := old.(string), new.(string); old != "" && new != old {
return true
}
customdiff.ForceNewIfChange(
"endpoint_details.0.vpc_id",
func(_ context.Context, old, new, meta interface{}) bool {
// "InvalidRequestException: Changing VpcId is not supported".
if old, new := old.(string), new.(string); old != "" && new != old {
return true
}

return false
}),
return false
},
),
),

Schema: map[string]*schema.Schema{
Expand All @@ -62,6 +65,12 @@ func ResourceServer() *schema.Resource {
Optional: true,
},

"function": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: verify.ValidARN,
},

"domain": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -101,10 +110,15 @@ func ResourceServer() *schema.Resource {
ConflictsWith: []string{"endpoint_details.0.vpc_endpoint_id"},
},
"vpc_endpoint_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ConflictsWith: []string{"endpoint_details.0.address_allocation_ids", "endpoint_details.0.security_group_ids", "endpoint_details.0.subnet_ids", "endpoint_details.0.vpc_id"},
Type: schema.TypeString,
Optional: true,
Computed: true,
ConflictsWith: []string{
"endpoint_details.0.address_allocation_ids",
"endpoint_details.0.security_group_ids",
"endpoint_details.0.subnet_ids",
"endpoint_details.0.vpc_id",
},
},
"vpc_id": {
Type: schema.TypeString,
Expand Down Expand Up @@ -210,6 +224,14 @@ func resourceServerCreate(d *schema.ResourceData, meta interface{}) error {
input.IdentityProviderDetails.DirectoryId = aws.String(v.(string))
}

if v, ok := d.GetOk("function"); ok {
if input.IdentityProviderDetails == nil {
input.IdentityProviderDetails = &transfer.IdentityProviderDetails{}
}

input.IdentityProviderDetails.Function = aws.String(v.(string))
}

if v, ok := d.GetOk("domain"); ok {
input.Domain = aws.String(v.(string))
}
Expand Down Expand Up @@ -271,7 +293,6 @@ func resourceServerCreate(d *schema.ResourceData, meta interface{}) error {

log.Printf("[DEBUG] Creating Transfer Server: %s", input)
output, err := conn.CreateServer(input)

if err != nil {
return fmt.Errorf("error creating Transfer Server: %w", err)
}
Expand Down Expand Up @@ -330,21 +351,28 @@ func resourceServerRead(d *schema.ResourceData, meta interface{}) error {
d.Set("certificate", output.Certificate)
if output.IdentityProviderDetails != nil {
d.Set("directory_id", output.IdentityProviderDetails.DirectoryId)
d.Set("function", output.IdentityProviderDetails.Function)
} else {
d.Set("directory_id", "")
d.Set("function", "")
}
d.Set("domain", output.Domain)
d.Set("endpoint", meta.(*conns.AWSClient).RegionalHostname(fmt.Sprintf("%s.server.transfer", d.Id())))
if output.EndpointDetails != nil {
securityGroupIDs := make([]*string, 0)

// Security Group IDs are not returned for VPC endpoints.
if aws.StringValue(output.EndpointType) == transfer.EndpointTypeVpc && len(output.EndpointDetails.SecurityGroupIds) == 0 {
if aws.StringValue(output.EndpointType) == transfer.EndpointTypeVpc &&
len(output.EndpointDetails.SecurityGroupIds) == 0 {
vpcEndpointID := aws.StringValue(output.EndpointDetails.VpcEndpointId)
output, err := tfec2.FindVPCEndpointByID(meta.(*conns.AWSClient).EC2Conn, vpcEndpointID)

if err != nil {
return fmt.Errorf("error reading Transfer Server (%s) VPC Endpoint (%s): %w", d.Id(), vpcEndpointID, err)
return fmt.Errorf(
"error reading Transfer Server (%s) VPC Endpoint (%s): %w",
d.Id(),
vpcEndpointID,
err,
)
}

for _, group := range output.Groups {
Expand Down Expand Up @@ -377,7 +405,7 @@ func resourceServerRead(d *schema.ResourceData, meta interface{}) error {

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

//lintignore:AWSR002
// lintignore:AWSR002
if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil {
return fmt.Errorf("error setting tags: %w", err)
}
Expand Down Expand Up @@ -484,13 +512,22 @@ func resourceServerUpdate(d *schema.ResourceData, meta interface{}) error {

log.Printf("[DEBUG] Updating VPC Endpoint: %s", input)
if _, err := conn.ModifyVpcEndpoint(input); err != nil {
return fmt.Errorf("error updating Transfer Server (%s) VPC Endpoint (%s): %w", d.Id(), vpcEndpointID, err)
return fmt.Errorf(
"error updating Transfer Server (%s) VPC Endpoint (%s): %w",
d.Id(),
vpcEndpointID,
err,
)
}

_, err := tfec2.WaitVPCEndpointAvailable(conn, vpcEndpointID, tfec2.VPCEndpointCreationTimeout)

if err != nil {
return fmt.Errorf("error waiting for Transfer Server (%s) VPC Endpoint (%s) to become available: %w", d.Id(), vpcEndpointID, err)
return fmt.Errorf(
"error waiting for Transfer Server (%s) VPC Endpoint (%s) to become available: %w",
d.Id(),
vpcEndpointID,
err,
)
}
}
}
Expand All @@ -515,6 +552,10 @@ func resourceServerUpdate(d *schema.ResourceData, meta interface{}) error {
identityProviderDetails.DirectoryId = aws.String(attr.(string))
}

if attr, ok := d.GetOk("function"); ok {
identityProviderDetails.Function = aws.String(attr.(string))
}

if attr, ok := d.GetOk("invocation_role"); ok {
identityProviderDetails.InvocationRole = aws.String(attr.(string))
}
Expand Down Expand Up @@ -597,7 +638,8 @@ func resourceServerUpdate(d *schema.ResourceData, meta interface{}) error {
func resourceServerDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).TransferConn

if d.Get("force_destroy").(bool) && d.Get("identity_provider_type").(string) == transfer.IdentityProviderTypeServiceManaged {
if d.Get("force_destroy").(bool) &&
d.Get("identity_provider_type").(string) == transfer.IdentityProviderTypeServiceManaged {
input := &transfer.ListUsersInput{
ServerId: aws.String(d.Id()),
}
Expand All @@ -610,7 +652,6 @@ func resourceServerDelete(d *schema.ResourceData, meta interface{}) error {

for _, user := range page.Users {
err := transferUserDelete(conn, d.Id(), aws.StringValue(user.UserName))

if err != nil {
log.Printf("[ERROR] %s", err)
deletionErrs = multierror.Append(deletionErrs, err)
Expand All @@ -621,7 +662,6 @@ func resourceServerDelete(d *schema.ResourceData, meta interface{}) error {

return !lastPage
})

if err != nil {
deletionErrs = multierror.Append(deletionErrs, fmt.Errorf("error listing Transfer Users: %w", err))
}
Expand Down Expand Up @@ -685,7 +725,10 @@ func expandTransferEndpointDetails(tfMap map[string]interface{}) *transfer.Endpo
return apiObject
}

func flattenTransferEndpointDetails(apiObject *transfer.EndpointDetails, securityGroupIDs []*string) map[string]interface{} {
func flattenTransferEndpointDetails(
apiObject *transfer.EndpointDetails,
securityGroupIDs []*string,
) map[string]interface{} {
if apiObject == nil {
return nil
}
Expand Down Expand Up @@ -760,7 +803,11 @@ func updateTransferServer(conn *transfer.Transfer, input *transfer.UpdateServerI
err := resource.Retry(tfec2.VPCEndpointCreationTimeout, func() *resource.RetryError {
_, err := conn.UpdateServer(input)

if tfawserr.ErrMessageContains(err, transfer.ErrCodeConflictException, "VPC Endpoint state is not yet available") {
if tfawserr.ErrMessageContains(
err,
transfer.ErrCodeConflictException,
"VPC Endpoint state is not yet available",
) {
return resource.RetryableError(err)
}

Expand Down
13 changes: 13 additions & 0 deletions website/docs/r/transfer_server.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ resource "aws_transfer_server" "example" {
}
```

### AWS Lambda authentication

```terraform
resource "aws_transfer_server" "example" {
identity_provider_type = "AWS_LAMBDA"
identity_provider_details {
function = aws_lambda_identity_provider.example.arn
}
}
```

### Protocols

```terraform
Expand Down Expand Up @@ -91,6 +103,7 @@ The following arguments are supported:
* `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. Using `AWS_DIRECTORY_SERVICE` will allow for authentication against AWS Managed Active Directory or Microsoft Active Directory in your on-premises environment, or in AWS using AD Connectors.
* `directory_id` - (Optional) The directory service ID of the directory service you want to connect to with an `identity_provider_type` of `AWS_DIRECTORY_SERVICE`.
* `function` - (Optional) The ARN for a lambda function to use for the Identity provider.
* `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`. This option only applies to servers configured with a `SERVICE_MANAGED` `identity_provider_type`.
* `security_policy_name` - (Optional) Specifies the name of the security policy that is attached to the server. Possible values are `TransferSecurityPolicy-2018-11`, `TransferSecurityPolicy-2020-06`, and `TransferSecurityPolicy-FIPS-2020-06`. Default value is: `TransferSecurityPolicy-2018-11`.
Expand Down

0 comments on commit 4aa2a2d

Please sign in to comment.