From 430d003cc800ac5093952a21256f785fbf3a2499 Mon Sep 17 00:00:00 2001 From: Michael Gilliland Date: Sun, 6 Oct 2019 17:48:07 -0400 Subject: [PATCH 1/7] Fix test name. --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b578620e755..a77827bbea0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,10 @@ ENHANCEMENTS: * resource/aws_emr_instance_group: Add `configurations_json` argument ([#10426](https://github.com/terraform-providers/terraform-provider-aws/issues/10426)) +FEATURES: + +* **New Resource:** `aws_quicksight_users` + BUG FIXES: * provider: Fix session handling to correctly validate and use assume_role credentials ([#10379](https://github.com/terraform-providers/terraform-provider-aws/issues/10379)) From 62e02972d4ca4c41ea5bea89ef167dec032ccfde Mon Sep 17 00:00:00 2001 From: Michael Gilliland Date: Thu, 10 Oct 2019 17:58:56 -0400 Subject: [PATCH 2/7] Address review comments for `aws_quicksight_user` by - Using go-sdk constants - Running, fixing and cleaning tests Here's my current test output ``` bash AWS_DEFAULT_REGION=us-east-1 AWS_PROFILE=default TF_ACC=1 go test ./aws -v -timeout 120m -parallel 20 -run='TestAccAWSQuickSightUser_' ``` > go: finding github.com/terraform-providers/terraform-provider-tls > v2.1.1+incompatible > go: finding github.com/terraform-providers/terraform-provider-tls > v2.1.1+incompatible > === RUN TestAccAWSQuickSightUser_basic > === PAUSE TestAccAWSQuickSightUser_basic > === RUN TestAccAWSQuickSightUser_withInvalidFormattedEmailStillWorks > === PAUSE TestAccAWSQuickSightUser_withInvalidFormattedEmailStillWorks > === RUN TestAccAWSQuickSightUser_disappears > === PAUSE TestAccAWSQuickSightUser_disappears > === CONT TestAccAWSQuickSightUser_basic > === CONT TestAccAWSQuickSightUser_disappears > === CONT TestAccAWSQuickSightUser_withInvalidFormattedEmailStillWorks > --- PASS: TestAccAWSQuickSightUser_disappears (14.19s) > --- PASS: TestAccAWSQuickSightUser_basic (25.44s) > --- PASS: TestAccAWSQuickSightUser_withInvalidFormattedEmailStillWorks > (26.08s) > PASS > ok github.com/terraform-providers/terraform-provider-aws/aws > 26.109s Note that the tests exposed that the `describe-user` QuickSight API does not return the schema that is documented. For example, when I run it via the CLI I get ``` json { "RequestId": "some-guid-looking-thing", "Status": 200, "User": { "Arn": "arn:aws:quicksight:us-west-2:01234567890:user/default/some_user", "PrincipalId": "federated/iam/some_user", "Email": "someemail@pfake.com", "Active": true, "Role": "ADMIN", "UserName": "some_user" } } ``` Notice that `IdentityType` is missing though documented! This means that I had to remove it from attributes and remove the importer :( since we can't reconcile that value. If there's a way around this please let me know. Also note that I couldn't use `acctest.RandomWithPrefix` because the resultant username was not valid in QuickSight. --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a77827bbea0..b578620e755 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,10 +26,6 @@ ENHANCEMENTS: * resource/aws_emr_instance_group: Add `configurations_json` argument ([#10426](https://github.com/terraform-providers/terraform-provider-aws/issues/10426)) -FEATURES: - -* **New Resource:** `aws_quicksight_users` - BUG FIXES: * provider: Fix session handling to correctly validate and use assume_role credentials ([#10379](https://github.com/terraform-providers/terraform-provider-aws/issues/10379)) From 50cec01a8b2c6811af6406154a1c8792821079ac Mon Sep 17 00:00:00 2001 From: Derek Helmick Date: Thu, 3 Oct 2019 21:09:30 -0400 Subject: [PATCH 3/7] aws_datasync_location_smb addition; docs and test for the same. --- aws/datasync.go | 26 + aws/provider.go | 1 + aws/resource_aws_datasync_location_smb.go | 247 ++++++++ ...resource_aws_datasync_location_smb_test.go | 533 ++++++++++++++++++ .../r/datasync_location_smb.html.markdown | 59 ++ 5 files changed, 866 insertions(+) create mode 100644 aws/resource_aws_datasync_location_smb.go create mode 100644 aws/resource_aws_datasync_location_smb_test.go create mode 100644 website/docs/r/datasync_location_smb.html.markdown diff --git a/aws/datasync.go b/aws/datasync.go index 046765d60f8..f81403cc9e9 100644 --- a/aws/datasync.go +++ b/aws/datasync.go @@ -33,6 +33,20 @@ func expandDataSyncEc2Config(l []interface{}) *datasync.Ec2Config { return ec2Config } +func expandDataSyncSmbMountOptions(l []interface{}) *datasync.SmbMountOptions { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + smbMountOptions := &datasync.SmbMountOptions{ + Version: aws.String(m["version"].(string)), + } + + return smbMountOptions +} + func expandDataSyncOnPremConfig(l []interface{}) *datasync.OnPremConfig { if len(l) == 0 || l[0] == nil { return nil @@ -99,6 +113,18 @@ func flattenDataSyncEc2Config(ec2Config *datasync.Ec2Config) []interface{} { return []interface{}{m} } +func flattenDataSyncSmbMountOptions(mountOptions *datasync.SmbMountOptions) []interface{} { + if mountOptions == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "version": aws.StringValue(mountOptions.Version), + } + + return []interface{}{m} +} + func flattenDataSyncOnPremConfig(onPremConfig *datasync.OnPremConfig) []interface{} { if onPremConfig == nil { return []interface{}{} diff --git a/aws/provider.go b/aws/provider.go index 587e2013734..7ef74bd7a83 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -409,6 +409,7 @@ func Provider() terraform.ResourceProvider { "aws_datasync_location_efs": resourceAwsDataSyncLocationEfs(), "aws_datasync_location_nfs": resourceAwsDataSyncLocationNfs(), "aws_datasync_location_s3": resourceAwsDataSyncLocationS3(), + "aws_datasync_location_smb": resourceAwsDataSyncLocationSmb(), "aws_datasync_task": resourceAwsDataSyncTask(), "aws_dax_cluster": resourceAwsDaxCluster(), "aws_dax_parameter_group": resourceAwsDaxParameterGroup(), diff --git a/aws/resource_aws_datasync_location_smb.go b/aws/resource_aws_datasync_location_smb.go new file mode 100644 index 00000000000..25d0e7cad73 --- /dev/null +++ b/aws/resource_aws_datasync_location_smb.go @@ -0,0 +1,247 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/datasync" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" +) + +func resourceAwsDataSyncLocationSmb() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsDataSyncLocationSmbCreate, + Read: resourceAwsDataSyncLocationSmbRead, + Update: resourceAwsDataSyncLocationSmbUpdate, + Delete: resourceAwsDataSyncLocationSmbDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "agent_arns": { + Type: schema.TypeSet, + Required: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "domain": { + Type: schema.TypeString, + Computed: true, + Optional: true, + }, + "mount_options": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + // Ignore missing config block (stolen from aws_ecs_service) + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + if old == "1" && new == "0" { + return true + } + return false + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "version": { + Type: schema.TypeString, + Default: "AUTOMATIC", + Optional: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + }, + }, + }, + "password": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + }, + "server_hostname": { + Type: schema.TypeString, + Required: true, + }, + "subdirectory": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + /*// Ignore missing trailing slash + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + if new == "/" { + return false + } + if strings.TrimSuffix(old, "/") == strings.TrimSuffix(new, "/") { + return true + } + return false + }, + */ + }, + "tags": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "uri": { + Type: schema.TypeString, + Computed: true, + }, + "user": { + Type: schema.TypeString, + Required: true, + }, + }, + } +} + +func resourceAwsDataSyncLocationSmbCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).datasyncconn + + input := &datasync.CreateLocationSmbInput{ + AgentArns: expandStringSet(d.Get("agent_arns").(*schema.Set)), + MountOptions: expandDataSyncSmbMountOptions(d.Get("mount_options").([]interface{})), + Password: aws.String(d.Get("password").(string)), + ServerHostname: aws.String(d.Get("server_hostname").(string)), + Subdirectory: aws.String(d.Get("subdirectory").(string)), + Tags: expandDataSyncTagListEntry(d.Get("tags").(map[string]interface{})), + User: aws.String(d.Get("user").(string)), + } + + if v, ok := d.GetOk("domain"); ok { + input.Domain = aws.String(v.(string)) + } + + log.Printf("[DEBUG] Creating DataSync Location SMB: %s", input) + output, err := conn.CreateLocationSmb(input) + if err != nil { + return fmt.Errorf("error creating DataSync Location SMB: %s", err) + } + + d.SetId(aws.StringValue(output.LocationArn)) + + return resourceAwsDataSyncLocationSmbRead(d, meta) +} + +func resourceAwsDataSyncLocationSmbRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).datasyncconn + + input := &datasync.DescribeLocationSmbInput{ + LocationArn: aws.String(d.Id()), + } + + log.Printf("[DEBUG] Reading DataSync Location SMB: %s", input) + output, err := conn.DescribeLocationSmb(input) + + if isAWSErr(err, "InvalidRequestException", "not found") { + log.Printf("[WARN] DataSync Location SMB %q not found - removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading DataSync Location SMB (%s): %s", d.Id(), err) + } + + tagsInput := &datasync.ListTagsForResourceInput{ + ResourceArn: output.LocationArn, + } + + log.Printf("[DEBUG] Reading DataSync Location SMB tags: %s", tagsInput) + tagsOutput, err := conn.ListTagsForResource(tagsInput) + + if err != nil { + return fmt.Errorf("error reading DataSync Location SMB (%s) tags: %s", d.Id(), err) + } + + subdirectory, err := dataSyncParseLocationURI(aws.StringValue(output.LocationUri)) + + if err != nil { + return fmt.Errorf("error parsing Location SMB (%s) URI (%s): %s", d.Id(), aws.StringValue(output.LocationUri), err) + } + + d.Set("agent_arns", schema.NewSet(schema.HashString, flattenStringList(output.AgentArns))) + + d.Set("arn", output.LocationArn) + + d.Set("domain", output.Domain) + + if err := d.Set("mount_options", flattenDataSyncSmbMountOptions(output.MountOptions)); err != nil { + return fmt.Errorf("error setting mount_options: %s", err) + } + + d.Set("subdirectory", subdirectory) + + if err := d.Set("tags", flattenDataSyncTagListEntry(tagsOutput.Tags)); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + + d.Set("user", output.User) + + d.Set("uri", output.LocationUri) + + return nil +} + +func resourceAwsDataSyncLocationSmbUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).datasyncconn + + if d.HasChange("tags") { + oldRaw, newRaw := d.GetChange("tags") + createTags, removeTags := dataSyncTagsDiff(expandDataSyncTagListEntry(oldRaw.(map[string]interface{})), expandDataSyncTagListEntry(newRaw.(map[string]interface{}))) + + if len(removeTags) > 0 { + input := &datasync.UntagResourceInput{ + Keys: dataSyncTagsKeys(removeTags), + ResourceArn: aws.String(d.Id()), + } + + log.Printf("[DEBUG] Untagging DataSync Location SMB: %s", input) + if _, err := conn.UntagResource(input); err != nil { + return fmt.Errorf("error untagging DataSync Location SMB (%s): %s", d.Id(), err) + } + } + + if len(createTags) > 0 { + input := &datasync.TagResourceInput{ + ResourceArn: aws.String(d.Id()), + Tags: createTags, + } + + log.Printf("[DEBUG] Tagging DataSync Location SMB: %s", input) + if _, err := conn.TagResource(input); err != nil { + return fmt.Errorf("error tagging DataSync Location SMB (%s): %s", d.Id(), err) + } + } + } + + return resourceAwsDataSyncLocationSmbRead(d, meta) +} + +func resourceAwsDataSyncLocationSmbDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).datasyncconn + + input := &datasync.DeleteLocationInput{ + LocationArn: aws.String(d.Id()), + } + + log.Printf("[DEBUG] Deleting DataSync Location SMB: %s", input) + _, err := conn.DeleteLocation(input) + + if isAWSErr(err, "InvalidRequestException", "not found") { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting DataSync Location SMB (%s): %s", d.Id(), err) + } + + return nil +} diff --git a/aws/resource_aws_datasync_location_smb_test.go b/aws/resource_aws_datasync_location_smb_test.go new file mode 100644 index 00000000000..8c5010ceffa --- /dev/null +++ b/aws/resource_aws_datasync_location_smb_test.go @@ -0,0 +1,533 @@ +package aws + +import ( + "errors" + "fmt" + "log" + "regexp" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/datasync" + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func init() { + resource.AddTestSweepers("aws_datasync_location_smb", &resource.Sweeper{ + Name: "aws_datasync_location_smb", + F: testSweepDataSyncLocationSmbs, + }) +} + +func testSweepDataSyncLocationSmbs(region string) error { + client, err := sharedClientForRegion(region) + if err != nil { + return fmt.Errorf("error getting client: %s", err) + } + conn := client.(*AWSClient).datasyncconn + + input := &datasync.ListLocationsInput{} + for { + output, err := conn.ListLocations(input) + + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping DataSync Location SMB sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("Error retrieving DataSync Location SMBs: %s", err) + } + + if len(output.Locations) == 0 { + log.Print("[DEBUG] No DataSync Location SMBs to sweep") + return nil + } + + for _, location := range output.Locations { + uri := aws.StringValue(location.LocationUri) + if !strings.HasPrefix(uri, "smb://") { + log.Printf("[INFO] Skipping DataSync Location SMB: %s", uri) + continue + } + log.Printf("[INFO] Deleting DataSync Location SMB: %s", uri) + input := &datasync.DeleteLocationInput{ + LocationArn: location.LocationArn, + } + + _, err := conn.DeleteLocation(input) + + if isAWSErr(err, "InvalidRequestException", "not found") { + continue + } + + if err != nil { + log.Printf("[ERROR] Failed to delete DataSync Location SMB (%s): %s", uri, err) + } + } + + if aws.StringValue(output.NextToken) == "" { + break + } + + input.NextToken = output.NextToken + } + + return nil +} + +func TestAccAWSDataSyncLocationSmb_basic(t *testing.T) { + var locationSmb1 datasync.DescribeLocationSmbOutput + + resourceName := "aws_datasync_location_smb.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSDataSync(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDataSyncLocationSmbDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDataSyncLocationSmbConfig(), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDataSyncLocationSmbExists(resourceName, &locationSmb1), + + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "datasync", regexp.MustCompile(`location/loc-.+`)), + resource.TestCheckResourceAttr(resourceName, "agent_arns.#", "1"), + resource.TestCheckResourceAttr(resourceName, "mount_options.#", "1"), + resource.TestCheckResourceAttr(resourceName, "mount_options.0.version", "AUTOMATIC"), + resource.TestCheckResourceAttr(resourceName, "user", "Guest"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestMatchResourceAttr(resourceName, "uri", regexp.MustCompile(`^smb://.+/`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"password", "server_hostname"}, + }, + }, + }) +} + +func TestAccAWSDataSyncLocationSmb_disappears(t *testing.T) { + var locationSmb1 datasync.DescribeLocationSmbOutput + resourceName := "aws_datasync_location_smb.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSDataSync(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDataSyncLocationSmbDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDataSyncLocationSmbConfig(), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDataSyncLocationSmbExists(resourceName, &locationSmb1), + testAccCheckAWSDataSyncLocationSmbDisappears(&locationSmb1), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSDataSyncLocationSmb_Tags(t *testing.T) { + var locationSmb1, locationSmb2, locationSmb3 datasync.DescribeLocationSmbOutput + resourceName := "aws_datasync_location_smb.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSDataSync(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDataSyncLocationSmbDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDataSyncLocationSmbConfigTags1("key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDataSyncLocationSmbExists(resourceName, &locationSmb1), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"password", "server_hostname"}, + }, + { + Config: testAccAWSDataSyncLocationSmbConfigTags2("key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDataSyncLocationSmbExists(resourceName, &locationSmb2), + testAccCheckAWSDataSyncLocationSmbNotRecreated(&locationSmb1, &locationSmb2), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSDataSyncLocationSmbConfigTags1("key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDataSyncLocationSmbExists(resourceName, &locationSmb3), + testAccCheckAWSDataSyncLocationSmbNotRecreated(&locationSmb2, &locationSmb3), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + }, + }) +} + +func testAccCheckAWSDataSyncLocationSmbDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).datasyncconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_datasync_location_smb" { + continue + } + + input := &datasync.DescribeLocationSmbInput{ + LocationArn: aws.String(rs.Primary.ID), + } + + _, err := conn.DescribeLocationSmb(input) + + if isAWSErr(err, "InvalidRequestException", "not found") { + return nil + } + + if err != nil { + return err + } + } + + return nil +} + +func testAccCheckAWSDataSyncLocationSmbExists(resourceName string, locationSmb *datasync.DescribeLocationSmbOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).datasyncconn + input := &datasync.DescribeLocationSmbInput{ + LocationArn: aws.String(rs.Primary.ID), + } + + output, err := conn.DescribeLocationSmb(input) + + if err != nil { + return err + } + + if output == nil { + return fmt.Errorf("Location %q does not exist", rs.Primary.ID) + } + + *locationSmb = *output + + return nil + } +} + +func testAccCheckAWSDataSyncLocationSmbDisappears(location *datasync.DescribeLocationSmbOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).datasyncconn + + input := &datasync.DeleteLocationInput{ + LocationArn: location.LocationArn, + } + + _, err := conn.DeleteLocation(input) + + return err + } +} + +func testAccCheckAWSDataSyncLocationSmbNotRecreated(i, j *datasync.DescribeLocationSmbOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + if aws.TimeValue(i.CreationTime) != aws.TimeValue(j.CreationTime) { + return errors.New("DataSync Location SMB was recreated") + } + + return nil + } +} + +func testAccAWSDataSyncLocationSmbConfigBase() string { + gatewayUid := acctest.RandString(5) + + return fmt.Sprintf(` +data "aws_ami" "aws_thinstaller" { + most_recent = true + owners = ["amazon"] + + filter { + name = "name" + values = ["aws-thinstaller-*"] + } +} + +data "aws_ami" "aws_datasync" { + most_recent = true + # I do not know why, but only in us-west-2 + # does the datasync ami _not_ have the amazon-alias. + # Reverting to amazon-owner id. + owners = ["633936118553"] + + filter { + name = "name" + values = ["aws-datasync-*"] + } +} + +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" + + tags = { + Name = "tf-acc-test-datasync-location-smb" + } +} + +resource "aws_subnet" "test" { + cidr_block = "10.0.0.0/24" + vpc_id = "${aws_vpc.test.id}" + + tags = { + Name = "tf-acc-test-datasync-location-smb" + } +} + +resource "aws_internet_gateway" "test" { + vpc_id = "${aws_vpc.test.id}" + + tags = { + Name = "tf-acc-test-datasync-location-smb" + } +} + +resource "aws_route_table" "test" { + vpc_id = "${aws_vpc.test.id}" + + route { + cidr_block = "0.0.0.0/0" + gateway_id = "${aws_internet_gateway.test.id}" + } + + tags = { + Name = "tf-acc-test-datasync-location-smb" + } +} + +resource "aws_route_table_association" "test" { + subnet_id = "${aws_subnet.test.id}" + route_table_id = "${aws_route_table.test.id}" +} + +resource "aws_iam_role" "test" { + assume_role_policy = < "/dev/xvdc" (forces new resource) + # We expect this data source value to change due to how Storage Gateway works. + lifecycle { + ignore_changes = ["disk_id"] + } + + disk_id = "${data.aws_storagegateway_local_disk.test.id}" + gateway_arn = "${aws_storagegateway_gateway.test.arn}" +} + +resource "aws_storagegateway_smb_file_share" "test" { + # Use GuestAccess to simplify testing + authentication = "GuestAccess" + gateway_arn = "${aws_storagegateway_gateway.test.arn}" + location_arn = "${aws_s3_bucket.test.arn}" + role_arn = "${aws_iam_role.test.arn}" + + # I'm not super sure why this depends_on sadness is required in + # the test framework but not the binary so... yolo! + depends_on = ["aws_storagegateway_cache.test"] +} + +resource "aws_instance" "test_datasync" { + depends_on = ["aws_internet_gateway.test"] + + ami = "${data.aws_ami.aws_datasync.id}" + associate_public_ip_address = true + + instance_type = "c5.large" + vpc_security_group_ids = ["${aws_security_group.test.id}"] + subnet_id = "${aws_subnet.test.id}" + + tags = { + Name = "tf-acc-test-datasync-smb" + } +} + +resource "aws_datasync_agent" "test" { + ip_address = "${aws_instance.test_datasync.public_ip}" + name = "datasyncsmb-%s" +} +`, gatewayUid, gatewayUid) +} + +func testAccAWSDataSyncLocationSmbConfig() string { + return testAccAWSDataSyncLocationSmbConfigBase() + fmt.Sprintf(` +resource "aws_datasync_location_smb" "test" { + user = "Guest" + password = "ZaphodBeeblebroxPW" + subdirectory = "/${aws_s3_bucket.test.id}/" + + server_hostname = "${aws_instance.test_datasync.public_ip}" + agent_arns = ["${aws_datasync_agent.test.arn}"] +} +`) +} + +func testAccAWSDataSyncLocationSmbConfigTags1(key1, value1 string) string { + return testAccAWSDataSyncLocationSmbConfigBase() + fmt.Sprintf(` +resource "aws_datasync_location_smb" "test" { + user = "Guest" + password = "ZaphodBeeblebroxPW" + subdirectory = "/${aws_s3_bucket.test.id}/" + + server_hostname = "${aws_instance.test_datasync.public_ip}" + agent_arns = ["${aws_datasync_agent.test.arn}"] + + tags = { + %q = %q + } +} +`, key1, value1) +} + +func testAccAWSDataSyncLocationSmbConfigTags2(key1, value1, key2, value2 string) string { + return testAccAWSDataSyncLocationSmbConfigBase() + fmt.Sprintf(` +resource "aws_datasync_location_smb" "test" { + user = "Guest" + password = "ZaphodBeeblebroxPW" + subdirectory = "/${aws_s3_bucket.test.id}/" + + server_hostname = "${aws_instance.test_datasync.public_ip}" + agent_arns = ["${aws_datasync_agent.test.arn}"] + + tags = { + %q = %q + %q = %q + } +} +`, key1, value1, key2, value2) +} diff --git a/website/docs/r/datasync_location_smb.html.markdown b/website/docs/r/datasync_location_smb.html.markdown new file mode 100644 index 00000000000..6b3355b2ef5 --- /dev/null +++ b/website/docs/r/datasync_location_smb.html.markdown @@ -0,0 +1,59 @@ +--- +layout: "aws" +page_title: "AWS: aws_datasync_location_smb" +description: |- + Manages an AWS DataSync SMB Location +--- + +# Resource: aws_datasync_location_SMB + +Manages a SMB Location within AWS DataSync. + +~> **NOTE:** The DataSync Agents must be available before creating this resource. + +## Example Usage + +```hcl +resource "aws_datasync_location_smb" "example" { + server_hostname = "smb.example.com" + subdirectory = "/exported/path" + + user = "Guest" + password = "ANotGreatPassword" + + agent_arns = ["${aws_datasync_agent.example.arn}"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `agent_arns` - (Required) A list of DataSync Agent ARNs with which this location will be associated. +* `domain` - (Optional) The name of the Windows domain the SMB server belongs to. +* `mount_options` - (Optional) +* `password` - (Required) The password of the user who can mount the share and has file permissions in the SMB. +* `server_hostname` - (Required) Specifies the IP address or DNS name of the SMB server. The DataSync Agent(s) use this to mount the SMB share. +* `subdirectory` - (Required) Subdirectory to perform actions as source or destination. Should be exported by the NFS server. +* `tags` - (Optional) Key-value pairs of resource tags to assign to the DataSync Location. +* `user` - (Required) The user who can mount the share and has file permissions in the SMB. + +### mount_options Argument Reference + +The following arguments are supported inside the `on_prem_config` configuration block: + +* `version` - (Optional) The specific SMB version that you want DataSync to use mounting your SMB share. Default: `AUTOMATIC` + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `arn` - Amazon Resource Name (ARN) of the DataSync Location. + +## Import + +`aws_datasync_location_smb` can be imported by using the DataSync Task Amazon Resource Name (ARN), e.g. + +``` +$ terraform import aws_datasync_location_smb.example arn:aws:datasync:us-east-1:123456789012:location/loc-12345678901234567 +``` From 07770c187903abc6d49ff274dd78ad7f4a4c0754 Mon Sep 17 00:00:00 2001 From: Derek Helmick Date: Mon, 18 Nov 2019 23:14:56 -0500 Subject: [PATCH 4/7] updates r-datasync_location_smb per required changes --- aws/resource_aws_datasync_location_smb.go | 69 ++++++------------- .../r/datasync_location_smb.html.markdown | 2 +- 2 files changed, 22 insertions(+), 49 deletions(-) diff --git a/aws/resource_aws_datasync_location_smb.go b/aws/resource_aws_datasync_location_smb.go index 25d0e7cad73..e9f39ef1af9 100644 --- a/aws/resource_aws_datasync_location_smb.go +++ b/aws/resource_aws_datasync_location_smb.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/service/datasync" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" ) func resourceAwsDataSyncLocationSmb() *schema.Resource { @@ -37,25 +38,23 @@ func resourceAwsDataSyncLocationSmb() *schema.Resource { Optional: true, }, "mount_options": { - Type: schema.TypeList, - Optional: true, - ForceNew: true, - MaxItems: 1, - // Ignore missing config block (stolen from aws_ecs_service) - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - if old == "1" && new == "0" { - return true - } - return false - }, + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + DiffSuppressFunc: suppressMissingOptionalConfigurationBlock, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "version": { - Type: schema.TypeString, - Default: "AUTOMATIC", - Optional: true, - ForceNew: true, - ValidateFunc: validation.NoZeroValues, + Type: schema.TypeString, + Default: datasync.SmbVersionAutomatic, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + datasync.SmbVersionAutomatic, + datasync.SmbVersionSmb2, + datasync.SmbVersionSmb3, + }, false), }, }, }, @@ -85,11 +84,7 @@ func resourceAwsDataSyncLocationSmb() *schema.Resource { }, */ }, - "tags": { - Type: schema.TypeMap, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, + "tags": tagsSchema(), "uri": { Type: schema.TypeString, Computed: true, @@ -111,7 +106,7 @@ func resourceAwsDataSyncLocationSmbCreate(d *schema.ResourceData, meta interface Password: aws.String(d.Get("password").(string)), ServerHostname: aws.String(d.Get("server_hostname").(string)), Subdirectory: aws.String(d.Get("subdirectory").(string)), - Tags: expandDataSyncTagListEntry(d.Get("tags").(map[string]interface{})), + Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().DatasyncTags(), User: aws.String(d.Get("user").(string)), } @@ -179,7 +174,7 @@ func resourceAwsDataSyncLocationSmbRead(d *schema.ResourceData, meta interface{} d.Set("subdirectory", subdirectory) - if err := d.Set("tags", flattenDataSyncTagListEntry(tagsOutput.Tags)); err != nil { + if err := d.Set("tags", keyvaluetags.DatasyncKeyValueTags(tagsOutput.Tags).IgnoreAws().Map()); err != nil { return fmt.Errorf("error setting tags: %s", err) } @@ -194,34 +189,12 @@ func resourceAwsDataSyncLocationSmbUpdate(d *schema.ResourceData, meta interface conn := meta.(*AWSClient).datasyncconn if d.HasChange("tags") { - oldRaw, newRaw := d.GetChange("tags") - createTags, removeTags := dataSyncTagsDiff(expandDataSyncTagListEntry(oldRaw.(map[string]interface{})), expandDataSyncTagListEntry(newRaw.(map[string]interface{}))) - - if len(removeTags) > 0 { - input := &datasync.UntagResourceInput{ - Keys: dataSyncTagsKeys(removeTags), - ResourceArn: aws.String(d.Id()), - } - - log.Printf("[DEBUG] Untagging DataSync Location SMB: %s", input) - if _, err := conn.UntagResource(input); err != nil { - return fmt.Errorf("error untagging DataSync Location SMB (%s): %s", d.Id(), err) - } - } - - if len(createTags) > 0 { - input := &datasync.TagResourceInput{ - ResourceArn: aws.String(d.Id()), - Tags: createTags, - } + o, n := d.GetChange("tags") - log.Printf("[DEBUG] Tagging DataSync Location SMB: %s", input) - if _, err := conn.TagResource(input); err != nil { - return fmt.Errorf("error tagging DataSync Location SMB (%s): %s", d.Id(), err) - } + if err := keyvaluetags.DatasyncUpdateTags(conn, d.Id(), o, n); err != nil { + return fmt.Errorf("error updating Datasync SMB location (%s) tags: %s", d.Id(), err) } } - return resourceAwsDataSyncLocationSmbRead(d, meta) } diff --git a/website/docs/r/datasync_location_smb.html.markdown b/website/docs/r/datasync_location_smb.html.markdown index 6b3355b2ef5..8a5a01ec095 100644 --- a/website/docs/r/datasync_location_smb.html.markdown +++ b/website/docs/r/datasync_location_smb.html.markdown @@ -31,7 +31,7 @@ The following arguments are supported: * `agent_arns` - (Required) A list of DataSync Agent ARNs with which this location will be associated. * `domain` - (Optional) The name of the Windows domain the SMB server belongs to. -* `mount_options` - (Optional) +* `mount_options` - (Optional) The mount options used by DataSync to access the SMB Server. Can be `AUTOMATIC`, `SMB2`, or `SMB3`. * `password` - (Required) The password of the user who can mount the share and has file permissions in the SMB. * `server_hostname` - (Required) Specifies the IP address or DNS name of the SMB server. The DataSync Agent(s) use this to mount the SMB share. * `subdirectory` - (Required) Subdirectory to perform actions as source or destination. Should be exported by the NFS server. From ef9aa0a9c3d3bf8577bf2b9a9c0e6707ea44746b Mon Sep 17 00:00:00 2001 From: Derek Helmick Date: Mon, 18 Nov 2019 23:36:13 -0500 Subject: [PATCH 5/7] updates r-datasync_location_smb for new documentation reqs --- website/docs/r/datasync_location_smb.html.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/r/datasync_location_smb.html.markdown b/website/docs/r/datasync_location_smb.html.markdown index 8a5a01ec095..c1b2c743f26 100644 --- a/website/docs/r/datasync_location_smb.html.markdown +++ b/website/docs/r/datasync_location_smb.html.markdown @@ -1,4 +1,5 @@ --- +subcategory: "DataSync" layout: "aws" page_title: "AWS: aws_datasync_location_smb" description: |- From bd69a7633d8fa3bf9737d92bb7d14ca7bc22610b Mon Sep 17 00:00:00 2001 From: Derek Helmick Date: Wed, 20 Nov 2019 14:26:46 -0500 Subject: [PATCH 6/7] r_datasync_location_smb doc fix --- website/docs/r/datasync_location_smb.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/datasync_location_smb.html.markdown b/website/docs/r/datasync_location_smb.html.markdown index c1b2c743f26..abac9ddb176 100644 --- a/website/docs/r/datasync_location_smb.html.markdown +++ b/website/docs/r/datasync_location_smb.html.markdown @@ -18,7 +18,7 @@ Manages a SMB Location within AWS DataSync. resource "aws_datasync_location_smb" "example" { server_hostname = "smb.example.com" subdirectory = "/exported/path" - + user = "Guest" password = "ANotGreatPassword" @@ -37,7 +37,7 @@ The following arguments are supported: * `server_hostname` - (Required) Specifies the IP address or DNS name of the SMB server. The DataSync Agent(s) use this to mount the SMB share. * `subdirectory` - (Required) Subdirectory to perform actions as source or destination. Should be exported by the NFS server. * `tags` - (Optional) Key-value pairs of resource tags to assign to the DataSync Location. -* `user` - (Required) The user who can mount the share and has file permissions in the SMB. +* `user` - (Required) The user who can mount the share and has file and folder permissions in the SMB share. ### mount_options Argument Reference From 0228303b05c6dee805ad34b7e8f73d9d81dc262f Mon Sep 17 00:00:00 2001 From: Derek Helmick Date: Mon, 3 Feb 2020 18:39:31 -0500 Subject: [PATCH 7/7] r-datasync_location_smb fixes --- aws/resource_aws_datasync_location_smb.go | 4 ++++ website/aws.erb | 3 +++ website/docs/r/datasync_location_smb.html.markdown | 10 +++++----- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/aws/resource_aws_datasync_location_smb.go b/aws/resource_aws_datasync_location_smb.go index e9f39ef1af9..6acc75790a7 100644 --- a/aws/resource_aws_datasync_location_smb.go +++ b/aws/resource_aws_datasync_location_smb.go @@ -36,6 +36,7 @@ func resourceAwsDataSyncLocationSmb() *schema.Resource { Type: schema.TypeString, Computed: true, Optional: true, + ForceNew: true, }, "mount_options": { Type: schema.TypeList, @@ -63,10 +64,12 @@ func resourceAwsDataSyncLocationSmb() *schema.Resource { Type: schema.TypeString, Required: true, Sensitive: true, + ForceNew: true, }, "server_hostname": { Type: schema.TypeString, Required: true, + ForceNew: true, }, "subdirectory": { Type: schema.TypeString, @@ -92,6 +95,7 @@ func resourceAwsDataSyncLocationSmb() *schema.Resource { "user": { Type: schema.TypeString, Required: true, + ForceNew: true, }, }, } diff --git a/website/aws.erb b/website/aws.erb index f3e1fe33ce2..aaca92d0c6a 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -793,6 +793,9 @@
  • aws_datasync_location_s3
  • +
  • + aws_datasync_location_smb +
  • aws_datasync_task
  • diff --git a/website/docs/r/datasync_location_smb.html.markdown b/website/docs/r/datasync_location_smb.html.markdown index abac9ddb176..2440441bde2 100644 --- a/website/docs/r/datasync_location_smb.html.markdown +++ b/website/docs/r/datasync_location_smb.html.markdown @@ -6,7 +6,7 @@ description: |- Manages an AWS DataSync SMB Location --- -# Resource: aws_datasync_location_SMB +# Resource: aws_datasync_location_smb Manages a SMB Location within AWS DataSync. @@ -32,7 +32,7 @@ The following arguments are supported: * `agent_arns` - (Required) A list of DataSync Agent ARNs with which this location will be associated. * `domain` - (Optional) The name of the Windows domain the SMB server belongs to. -* `mount_options` - (Optional) The mount options used by DataSync to access the SMB Server. Can be `AUTOMATIC`, `SMB2`, or `SMB3`. +* `mount_options` - (Optional) Configuration block containing mount options used by DataSync to access the SMB Server. Can be `AUTOMATIC`, `SMB2`, or `SMB3`. * `password` - (Required) The password of the user who can mount the share and has file permissions in the SMB. * `server_hostname` - (Required) Specifies the IP address or DNS name of the SMB server. The DataSync Agent(s) use this to mount the SMB share. * `subdirectory` - (Required) Subdirectory to perform actions as source or destination. Should be exported by the NFS server. @@ -41,9 +41,9 @@ The following arguments are supported: ### mount_options Argument Reference -The following arguments are supported inside the `on_prem_config` configuration block: +The following arguments are supported inside the `mount_options` configuration block: -* `version` - (Optional) The specific SMB version that you want DataSync to use mounting your SMB share. Default: `AUTOMATIC` +* `version` - (Optional) The specific SMB version that you want DataSync to use for mounting your SMB share. Valid values: `AUTOMATIC`, `SMB2`, and `SMB3`. Default: `AUTOMATIC` ## Attribute Reference @@ -53,7 +53,7 @@ In addition to all arguments above, the following attributes are exported: ## Import -`aws_datasync_location_smb` can be imported by using the DataSync Task Amazon Resource Name (ARN), e.g. +`aws_datasync_location_smb` can be imported by using the Amazon Resource Name (ARN), e.g. ``` $ terraform import aws_datasync_location_smb.example arn:aws:datasync:us-east-1:123456789012:location/loc-12345678901234567