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

r/transfer_user - add support for posix_profile #19693

Merged
merged 3 commits into from
Jun 8, 2021
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/19693.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_transfer_user: Add `posix_profile` argument.
```
29 changes: 29 additions & 0 deletions aws/internal/service/transfer/finder/finder.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,32 @@ func ServerByID(conn *transfer.Transfer, id string) (*transfer.DescribedServer,

return output.Server, nil
}

func UserByID(conn *transfer.Transfer, serverId, userName string) (*transfer.DescribeUserOutput, error) {
input := &transfer.DescribeUserInput{
ServerId: aws.String(serverId),
UserName: aws.String(userName),
}

output, err := conn.DescribeUser(input)

if tfawserr.ErrCodeEquals(err, transfer.ErrCodeResourceNotFoundException) {
return nil, &resource.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

if output == nil || output.User == nil {
return nil, &resource.NotFoundError{
Message: "Empty result",
LastRequest: input,
}
}

return output, nil
}
16 changes: 16 additions & 0 deletions aws/internal/service/transfer/waiter/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,19 @@ func ServerState(conn *transfer.Transfer, id string) resource.StateRefreshFunc {
return output, aws.StringValue(output.State), nil
}
}

func UserState(conn *transfer.Transfer, serverId, userName string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
output, err := finder.UserByID(conn, serverId, userName)

if tfresource.NotFound(err) {
return nil, "", nil
}

if err != nil {
return nil, "", err
}

return output, "Available", nil
}
}
18 changes: 18 additions & 0 deletions aws/internal/service/transfer/waiter/waiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

const (
ServerDeletedTimeout = 10 * time.Minute
UserDeletedTimeout = 10 * time.Minute
)

func ServerCreated(conn *transfer.Transfer, id string, timeout time.Duration) (*transfer.DescribedServer, error) {
Expand Down Expand Up @@ -78,3 +79,20 @@ func ServerStopped(conn *transfer.Transfer, id string, timeout time.Duration) (*

return nil, err
}

func UserDeleted(conn *transfer.Transfer, serverId, userName string) (*transfer.DescribedUser, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{"Available"},
Target: []string{},
Refresh: UserState(conn, serverId, userName),
Timeout: UserDeletedTimeout,
}

outputRaw, err := stateConf.WaitForState()

if output, ok := outputRaw.(*transfer.DescribedUser); ok {
return output, err
}

return nil, err
}
153 changes: 93 additions & 60 deletions aws/resource_aws_transfer_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ package aws
import (
"fmt"
"log"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/transfer"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
tftransfer "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/transfer"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/transfer/finder"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/transfer/waiter"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource"
)

func resourceAwsTransferUser() *schema.Resource {
Expand Down Expand Up @@ -69,6 +70,28 @@ func resourceAwsTransferUser() *schema.Resource {
ValidateFunc: validateIAMPolicyJson,
DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs,
},
"posix_profile": {
Type: schema.TypeList,
MaxItems: 1,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"gid": {
Type: schema.TypeInt,
Required: true,
},
"uid": {
Type: schema.TypeInt,
Required: true,
},
"secondary_gids": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeInt},
Optional: true,
},
},
},
},

"role": {
Type: schema.TypeString,
Expand Down Expand Up @@ -124,6 +147,10 @@ func resourceAwsTransferUserCreate(d *schema.ResourceData, meta interface{}) err
createOpts.HomeDirectoryMappings = expandAwsTransferHomeDirectoryMappings(attr.([]interface{}))
}

if attr, ok := d.GetOk("posix_profile"); ok {
createOpts.PosixProfile = expandTransferUserPosixUser(attr.([]interface{}))
}

if attr, ok := d.GetOk("policy"); ok {
createOpts.Policy = aws.String(attr.(string))
}
Expand Down Expand Up @@ -154,36 +181,34 @@ func resourceAwsTransferUserRead(d *schema.ResourceData, meta interface{}) error
return fmt.Errorf("error parsing Transfer User ID: %s", err)
}

descOpts := &transfer.DescribeUserInput{
UserName: aws.String(userName),
ServerId: aws.String(serverID),
resp, err := finder.UserByID(conn, serverID, userName)
if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] Transfer User (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

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 from state", userName, serverID)
d.SetId("")
return nil
}
return fmt.Errorf("error reading Transfer User (%s): %s", d.Id(), err)
return fmt.Errorf("error reading Transfer User (%s): %w", d.Id(), err)
}

user := resp.User
d.Set("server_id", resp.ServerId)
d.Set("user_name", resp.User.UserName)
d.Set("arn", resp.User.Arn)
d.Set("home_directory", resp.User.HomeDirectory)
d.Set("home_directory_type", resp.User.HomeDirectoryType)
d.Set("policy", resp.User.Policy)
d.Set("role", resp.User.Role)
d.Set("user_name", user.UserName)
d.Set("arn", user.Arn)
d.Set("home_directory", user.HomeDirectory)
d.Set("home_directory_type", user.HomeDirectoryType)
d.Set("policy", user.Policy)
d.Set("role", user.Role)

if err := d.Set("home_directory_mappings", flattenAwsTransferHomeDirectoryMappings(resp.User.HomeDirectoryMappings)); err != nil {
return fmt.Errorf("Error setting home_directory_mappings: %s", err)
if err := d.Set("home_directory_mappings", flattenAwsTransferHomeDirectoryMappings(user.HomeDirectoryMappings)); err != nil {
return fmt.Errorf("Error setting home_directory_mappings: %w", err)
}

tags := keyvaluetags.TransferKeyValueTags(resp.User.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig)
if err := d.Set("posix_profile", flattenTransferUserPosixUser(user.PosixProfile)); err != nil {
return fmt.Errorf("Error setting posix_profile: %w", err)
}
tags := keyvaluetags.TransferKeyValueTags(user.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig)

//lintignore:AWSR002
if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil {
Expand Down Expand Up @@ -219,6 +244,11 @@ func resourceAwsTransferUserUpdate(d *schema.ResourceData, meta interface{}) err
updateFlag = true
}

if d.HasChange("posix_profile") {
updateOpts.PosixProfile = expandTransferUserPosixUser(d.Get("posix_profile").([]interface{}))
updateFlag = true
}

if d.HasChange("home_directory_type") {
updateOpts.HomeDirectoryType = aws.String(d.Get("home_directory_type").(string))
updateFlag = true
Expand All @@ -242,14 +272,14 @@ func resourceAwsTransferUserUpdate(d *schema.ResourceData, meta interface{}) err
d.SetId("")
return nil
}
return fmt.Errorf("error updating Transfer User (%s): %s", d.Id(), err)
return fmt.Errorf("error updating Transfer User (%s): %w", d.Id(), err)
}
}

if d.HasChange("tags_all") {
o, n := d.GetChange("tags_all")
if err := keyvaluetags.TransferUpdateTags(conn, d.Get("arn").(string), o, n); err != nil {
return fmt.Errorf("error updating tags: %s", err)
return fmt.Errorf("error updating tags: %w", err)
}
}

Expand All @@ -260,7 +290,7 @@ func resourceAwsTransferUserDelete(d *schema.ResourceData, meta interface{}) err
conn := meta.(*AWSClient).transferconn
serverID, userName, err := tftransfer.UserParseResourceID(d.Id())
if err != nil {
return fmt.Errorf("error parsing Transfer User ID: %s", err)
return fmt.Errorf("error parsing Transfer User ID: %w", err)
}

delOpts := &transfer.DeleteUserInput{
Expand All @@ -275,45 +305,15 @@ func resourceAwsTransferUserDelete(d *schema.ResourceData, meta interface{}) err
if isAWSErr(err, transfer.ErrCodeResourceNotFoundException, "") {
return nil
}
return fmt.Errorf("error deleting Transfer User (%s) for Server(%s): %s", userName, serverID, err)
return fmt.Errorf("error deleting Transfer User (%s) for Server(%s): %w", userName, serverID, err)
}

if err := waitForTransferUserDeletion(conn, serverID, userName); err != nil {
return fmt.Errorf("error waiting for Transfer User (%s) for Server (%s): %s", userName, serverID, err)
}

return nil
}
_, err = waiter.UserDeleted(conn, serverID, userName)

func waitForTransferUserDeletion(conn *transfer.Transfer, serverID, userName string) error {
params := &transfer.DescribeUserInput{
ServerId: aws.String(serverID),
UserName: aws.String(userName),
}

err := resource.Retry(10*time.Minute, func() *resource.RetryError {
_, err := conn.DescribeUser(params)

if isAWSErr(err, transfer.ErrCodeResourceNotFoundException, "") {
return nil
}

if err != nil {
return resource.NonRetryableError(err)
}

return resource.RetryableError(fmt.Errorf("Transfer User (%s) for Server (%s) still exists", userName, serverID))
})

if isResourceTimeoutError(err) {
_, err = conn.DescribeUser(params)
}
if isAWSErr(err, transfer.ErrCodeResourceNotFoundException, "") {
return nil
}
if err != nil {
return fmt.Errorf("Error decoding transfer user ID: %s", err)
return fmt.Errorf("error waiting for Transfer User (%s) delete: %w", d.Id(), err)
}

return nil
}

Expand Down Expand Up @@ -344,3 +344,36 @@ func flattenAwsTransferHomeDirectoryMappings(mappings []*transfer.HomeDirectoryM
}
return l
}

func expandTransferUserPosixUser(pUser []interface{}) *transfer.PosixProfile {
if len(pUser) < 1 || pUser[0] == nil {
return nil
}

m := pUser[0].(map[string]interface{})

posixUser := &transfer.PosixProfile{
Gid: aws.Int64(int64(m["gid"].(int))),
Uid: aws.Int64(int64(m["uid"].(int))),
}

if v, ok := m["secondary_gids"].(*schema.Set); ok && len(v.List()) > 0 {
posixUser.SecondaryGids = expandInt64Set(v)
}

return posixUser
}

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

m := map[string]interface{}{
"gid": aws.Int64Value(posixUser.Gid),
"uid": aws.Int64Value(posixUser.Uid),
"secondary_gids": aws.Int64ValueSlice(posixUser.SecondaryGids),
}

return []interface{}{m}
}
Loading