diff --git a/aws/internal/service/workspaces/waiter/status.go b/aws/internal/service/workspaces/waiter/status.go new file mode 100644 index 000000000000..06a3fd1bc6db --- /dev/null +++ b/aws/internal/service/workspaces/waiter/status.go @@ -0,0 +1,43 @@ +package waiter + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/workspaces" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func DirectoryState(conn *workspaces.WorkSpaces, directoryID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := conn.DescribeWorkspaceDirectories(&workspaces.DescribeWorkspaceDirectoriesInput{ + DirectoryIds: aws.StringSlice([]string{directoryID}), + }) + if err != nil { + return nil, workspaces.WorkspaceDirectoryStateError, err + } + + if len(output.Directories) == 0 { + return output, workspaces.WorkspaceDirectoryStateDeregistered, nil + } + + directory := output.Directories[0] + return directory, aws.StringValue(directory.State), nil + } +} + +func WorkspaceState(conn *workspaces.WorkSpaces, workspaceID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := conn.DescribeWorkspaces(&workspaces.DescribeWorkspacesInput{ + WorkspaceIds: aws.StringSlice([]string{workspaceID}), + }) + if err != nil { + return nil, workspaces.WorkspaceStateError, err + } + + if len(output.Workspaces) == 0 { + return nil, workspaces.WorkspaceStateTerminated, nil + } + + workspace := output.Workspaces[0] + return workspace, aws.StringValue(workspace.State), nil + } +} diff --git a/aws/internal/service/workspaces/waiter/waiter.go b/aws/internal/service/workspaces/waiter/waiter.go new file mode 100644 index 000000000000..28fd0fd2d733 --- /dev/null +++ b/aws/internal/service/workspaces/waiter/waiter.go @@ -0,0 +1,151 @@ +package waiter + +import ( + "time" + + "github.com/aws/aws-sdk-go/service/workspaces" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +const ( + // Maximum amount of time to wait for a Directory to return Registered + DirectoryRegisteredTimeout = 10 * time.Minute + + // Maximum amount of time to wait for a Directory to return Deregistered + DirectoryDeregisteredTimeout = 10 * time.Minute + + // Maximum amount of time to wait for a WorkSpace to return Available + WorkspaceAvailableTimeout = 30 * time.Minute + + // Maximum amount of time to wait for a WorkSpace while returning Updating + WorkspaceUpdatingTimeout = 10 * time.Minute + + // Amount of time to delay before checking WorkSpace when updating + WorkspaceUpdatingDelay = 1 * time.Minute + + // Maximum amount of time to wait for a WorkSpace to return Terminated + WorkspaceTerminatedTimeout = 10 * time.Minute +) + +func DirectoryRegistered(conn *workspaces.WorkSpaces, directoryID string) (*workspaces.WorkspaceDirectory, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ + workspaces.WorkspaceDirectoryStateRegistering, + }, + Target: []string{workspaces.WorkspaceDirectoryStateRegistered}, + Refresh: DirectoryState(conn, directoryID), + Timeout: DirectoryRegisteredTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if v, ok := outputRaw.(*workspaces.WorkspaceDirectory); ok { + return v, err + } + + return nil, err +} + +func DirectoryDeregistered(conn *workspaces.WorkSpaces, directoryID string) (*workspaces.WorkspaceDirectory, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ + workspaces.WorkspaceDirectoryStateRegistering, + workspaces.WorkspaceDirectoryStateRegistered, + workspaces.WorkspaceDirectoryStateDeregistering, + }, + Target: []string{ + workspaces.WorkspaceDirectoryStateDeregistered, + }, + Refresh: DirectoryState(conn, directoryID), + Timeout: DirectoryDeregisteredTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if v, ok := outputRaw.(*workspaces.WorkspaceDirectory); ok { + return v, err + } + + return nil, err +} + +func WorkspaceAvailable(conn *workspaces.WorkSpaces, workspaceID string) (*workspaces.Workspace, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ + workspaces.WorkspaceStatePending, + workspaces.WorkspaceStateStarting, + }, + Target: []string{workspaces.WorkspaceStateAvailable}, + Refresh: WorkspaceState(conn, workspaceID), + Timeout: WorkspaceAvailableTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if v, ok := outputRaw.(*workspaces.Workspace); ok { + return v, err + } + + return nil, err +} + +func WorkspaceTerminated(conn *workspaces.WorkSpaces, workspaceID string) (*workspaces.Workspace, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ + workspaces.WorkspaceStatePending, + workspaces.WorkspaceStateAvailable, + workspaces.WorkspaceStateImpaired, + workspaces.WorkspaceStateUnhealthy, + workspaces.WorkspaceStateRebooting, + workspaces.WorkspaceStateStarting, + workspaces.WorkspaceStateRebuilding, + workspaces.WorkspaceStateRestoring, + workspaces.WorkspaceStateMaintenance, + workspaces.WorkspaceStateAdminMaintenance, + workspaces.WorkspaceStateSuspended, + workspaces.WorkspaceStateUpdating, + workspaces.WorkspaceStateStopping, + workspaces.WorkspaceStateStopped, + workspaces.WorkspaceStateTerminating, + workspaces.WorkspaceStateError, + }, + Target: []string{ + workspaces.WorkspaceStateTerminated, + }, + Refresh: WorkspaceState(conn, workspaceID), + Timeout: WorkspaceTerminatedTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if v, ok := outputRaw.(*workspaces.Workspace); ok { + return v, err + } + + return nil, err +} + +func WorkspaceUpdated(conn *workspaces.WorkSpaces, workspaceID string) (*workspaces.Workspace, error) { + // OperationInProgressException: The properties of this WorkSpace are currently under modification. Please try again in a moment. + // AWS Workspaces service doesn't change instance status to "Updating" during property modification. Respective AWS Support feature request has been created. Meanwhile, artificial delay is placed here as a workaround. + stateConf := &resource.StateChangeConf{ + Pending: []string{ + workspaces.WorkspaceStateUpdating, + }, + Target: []string{ + workspaces.WorkspaceStateAvailable, + workspaces.WorkspaceStateStopped, + }, + Refresh: WorkspaceState(conn, workspaceID), + Delay: WorkspaceUpdatingDelay, + Timeout: WorkspaceUpdatingTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if v, ok := outputRaw.(*workspaces.Workspace); ok { + return v, err + } + + return nil, err +} diff --git a/aws/resource_aws_workspaces_directory.go b/aws/resource_aws_workspaces_directory.go index b98a3f0404fe..9b5976ae1c44 100644 --- a/aws/resource_aws_workspaces_directory.go +++ b/aws/resource_aws_workspaces_directory.go @@ -3,14 +3,13 @@ package aws import ( "fmt" "log" - "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/workspaces" - "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/workspaces/waiter" ) func resourceAwsWorkspacesDirectory() *schema.Resource { @@ -131,31 +130,21 @@ func resourceAwsWorkspacesDirectoryCreate(d *schema.ResourceData, meta interface input.SubnetIds = expandStringSet(v.(*schema.Set)) } - log.Printf("[DEBUG] Regestering workspaces directory...\n%#v\n", *input) + log.Printf("[DEBUG] Regestering WorkSpaces Directory...\n%#v\n", *input) _, err := conn.RegisterWorkspaceDirectory(input) if err != nil { return err } d.SetId(directoryId) - log.Printf("[DEBUG] Waiting for workspaces directory %q to become registered...", d.Id()) - stateConf := &resource.StateChangeConf{ - Pending: []string{ - workspaces.WorkspaceDirectoryStateRegistering, - }, - Target: []string{workspaces.WorkspaceDirectoryStateRegistered}, - Refresh: workspacesDirectoryRefreshStateFunc(conn, directoryId), - PollInterval: 30 * time.Second, - Timeout: 10 * time.Minute, - } - - _, err = stateConf.WaitForState() + log.Printf("[DEBUG] Waiting for WorkSpaces Directory %q to become registered...", directoryId) + _, err = waiter.DirectoryRegistered(conn, directoryId) if err != nil { return fmt.Errorf("error registering directory: %s", err) } - log.Printf("[DEBUG] Workspaces directory %q is registered", d.Id()) + log.Printf("[DEBUG] WorkSpaces Directory %q is registered", directoryId) - log.Printf("[DEBUG] Modifying workspaces directory %q self-service permissions...", d.Id()) + log.Printf("[DEBUG] Modifying WorkSpaces Directory %q self-service permissions...", directoryId) if v, ok := d.GetOk("self_service_permissions"); ok { _, err := conn.ModifySelfservicePermissions(&workspaces.ModifySelfservicePermissionsInput{ ResourceId: aws.String(directoryId), @@ -165,7 +154,7 @@ func resourceAwsWorkspacesDirectoryCreate(d *schema.ResourceData, meta interface return fmt.Errorf("error setting self service permissions: %s", err) } } - log.Printf("[DEBUG] Workspaces directory %q self-service permissions are set", d.Id()) + log.Printf("[DEBUG] WorkSpaces Directory %q self-service permissions are set", directoryId) return resourceAwsWorkspacesDirectoryRead(d, meta) } @@ -174,36 +163,36 @@ func resourceAwsWorkspacesDirectoryRead(d *schema.ResourceData, meta interface{} conn := meta.(*AWSClient).workspacesconn ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - raw, state, err := workspacesDirectoryRefreshStateFunc(conn, d.Id())() + rawOutput, state, err := waiter.DirectoryState(conn, d.Id())() if err != nil { - return fmt.Errorf("error getting workspaces directory (%s): %s", d.Id(), err) + return fmt.Errorf("error getting WorkSpaces Directory (%s): %s", d.Id(), err) } if state == workspaces.WorkspaceDirectoryStateDeregistered { - log.Printf("[WARN] workspaces directory (%s) not found, removing from state", d.Id()) + log.Printf("[WARN] WorkSpaces Directory (%s) not found, removing from state", d.Id()) d.SetId("") return nil } - dir := raw.(*workspaces.WorkspaceDirectory) - d.Set("directory_id", dir.DirectoryId) - if err := d.Set("subnet_ids", flattenStringSet(dir.SubnetIds)); err != nil { + directory := rawOutput.(*workspaces.WorkspaceDirectory) + d.Set("directory_id", directory.DirectoryId) + if err := d.Set("subnet_ids", flattenStringSet(directory.SubnetIds)); err != nil { return fmt.Errorf("error setting subnet_ids: %s", err) } - d.Set("workspace_security_group_id", dir.WorkspaceSecurityGroupId) - d.Set("iam_role_id", dir.IamRoleId) - d.Set("registration_code", dir.RegistrationCode) - d.Set("directory_name", dir.DirectoryName) - d.Set("directory_type", dir.DirectoryType) - d.Set("alias", dir.Alias) - if err := d.Set("self_service_permissions", flattenSelfServicePermissions(dir.SelfservicePermissions)); err != nil { + d.Set("workspace_security_group_id", directory.WorkspaceSecurityGroupId) + d.Set("iam_role_id", directory.IamRoleId) + d.Set("registration_code", directory.RegistrationCode) + d.Set("directory_name", directory.DirectoryName) + d.Set("directory_type", directory.DirectoryType) + d.Set("alias", directory.Alias) + if err := d.Set("self_service_permissions", flattenSelfServicePermissions(directory.SelfservicePermissions)); err != nil { return fmt.Errorf("error setting self_service_permissions: %s", err) } - if err := d.Set("ip_group_ids", flattenStringSet(dir.IpGroupIds)); err != nil { + if err := d.Set("ip_group_ids", flattenStringSet(directory.IpGroupIds)); err != nil { return fmt.Errorf("error setting ip_group_ids: %s", err) } - if err := d.Set("dns_ip_addresses", flattenStringSet(dir.DnsIpAddresses)); err != nil { + if err := d.Set("dns_ip_addresses", flattenStringSet(directory.DnsIpAddresses)); err != nil { return fmt.Errorf("error setting dns_ip_addresses: %s", err) } @@ -223,7 +212,7 @@ func resourceAwsWorkspacesDirectoryUpdate(d *schema.ResourceData, meta interface conn := meta.(*AWSClient).workspacesconn if d.HasChange("self_service_permissions") { - log.Printf("[DEBUG] Modifying workspaces directory %q self-service permissions...", d.Id()) + log.Printf("[DEBUG] Modifying WorkSpaces Directory %q self-service permissions...", d.Id()) permissions := d.Get("self_service_permissions").([]interface{}) _, err := conn.ModifySelfservicePermissions(&workspaces.ModifySelfservicePermissionsInput{ @@ -233,7 +222,7 @@ func resourceAwsWorkspacesDirectoryUpdate(d *schema.ResourceData, meta interface if err != nil { return fmt.Errorf("error updating self service permissions: %s", err) } - log.Printf("[DEBUG] Workspaces directory %q self-service permissions are set", d.Id()) + log.Printf("[DEBUG] WorkSpaces Directory %q self-service permissions are set", d.Id()) } if d.HasChange("tags") { @@ -251,60 +240,31 @@ func resourceAwsWorkspacesDirectoryDelete(d *schema.ResourceData, meta interface err := workspacesDirectoryDelete(d.Id(), conn) if err != nil { - return fmt.Errorf("error deleting workspaces directory (%s): %s", d.Id(), err) + return fmt.Errorf("error deleting WorkSpaces Directory (%s): %s", d.Id(), err) } return nil } func workspacesDirectoryDelete(id string, conn *workspaces.WorkSpaces) error { - log.Printf("[DEBUG] Deregistering Workspace Directory %q", id) + log.Printf("[DEBUG] Deregistering WorkSpaces Directory %q", id) _, err := conn.DeregisterWorkspaceDirectory(&workspaces.DeregisterWorkspaceDirectoryInput{ DirectoryId: aws.String(id), }) if err != nil { - return fmt.Errorf("error deregistering Workspace Directory %q: %w", id, err) + return fmt.Errorf("error deregistering WorkSpaces Directory %q: %w", id, err) } - log.Printf("[DEBUG] Waiting for Workspace Directory %q to be deregistered", id) - stateConf := &resource.StateChangeConf{ - Pending: []string{ - workspaces.WorkspaceDirectoryStateRegistering, - workspaces.WorkspaceDirectoryStateRegistered, - workspaces.WorkspaceDirectoryStateDeregistering, - }, - Target: []string{ - workspaces.WorkspaceDirectoryStateDeregistered, - }, - Refresh: workspacesDirectoryRefreshStateFunc(conn, id), - PollInterval: 30 * time.Second, - Timeout: 10 * time.Minute, - } - _, err = stateConf.WaitForState() + log.Printf("[DEBUG] Waiting for WorkSpaces Directory %q to be deregistered", id) + _, err = waiter.DirectoryDeregistered(conn, id) if err != nil { - return fmt.Errorf("error waiting for Workspace Directory %q to be deregistered: %w", id, err) + return fmt.Errorf("error waiting for WorkSpaces Directory %q to be deregistered: %w", id, err) } - log.Printf("[DEBUG] Workspaces Directory %q is deregistered", id) + log.Printf("[DEBUG] WorkSpaces Directory %q is deregistered", id) return nil } -func workspacesDirectoryRefreshStateFunc(conn *workspaces.WorkSpaces, directoryID string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - resp, err := conn.DescribeWorkspaceDirectories(&workspaces.DescribeWorkspaceDirectoriesInput{ - DirectoryIds: []*string{aws.String(directoryID)}, - }) - if err != nil { - return nil, workspaces.WorkspaceDirectoryStateError, err - } - if len(resp.Directories) == 0 { - return resp, workspaces.WorkspaceDirectoryStateDeregistered, nil - } - directory := resp.Directories[0] - return directory, aws.StringValue(directory.State), nil - } -} - func expandSelfServicePermissions(permissions []interface{}) *workspaces.SelfservicePermissions { if len(permissions) == 0 || permissions[0] == nil { return nil diff --git a/aws/resource_aws_workspaces_directory_test.go b/aws/resource_aws_workspaces_directory_test.go index 1ddc73e588ff..03d741a9f56e 100644 --- a/aws/resource_aws_workspaces_directory_test.go +++ b/aws/resource_aws_workspaces_directory_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iam" "github.com/aws/aws-sdk-go/service/workspaces" multierror "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" @@ -52,36 +53,21 @@ func testSweepWorkspacesDirectories(region string) error { return errors } -// These tests need to be serialized, because they all rely on the IAM Role `workspaces_DefaultRole`. -func TestAccAwsWorkspacesDirectory(t *testing.T) { - testCases := map[string]func(t *testing.T){ - "basic": testAccAwsWorkspacesDirectory_basic, - "disappears": testAccAwsWorkspacesDirectory_disappears, - "subnetIds": testAccAwsWorkspacesDirectory_subnetIds, - "tags": testAccAwsWorkspacesDirectory_tags, - } - for name, tc := range testCases { - tc := tc - t.Run(name, func(t *testing.T) { - tc(t) - }) - } -} - -func testAccAwsWorkspacesDirectory_basic(t *testing.T) { +func TestAccAwsWorkspacesDirectory_basic(t *testing.T) { var v workspaces.WorkspaceDirectory - booster := acctest.RandString(8) + rName := acctest.RandString(8) + resourceName := "aws_workspaces_directory.main" directoryResourceName := "aws_directory_service_directory.main" - iamRoleResourceName := "aws_iam_role.workspaces-default" + iamRoleDataSourceName := "data.aws_iam_role.workspaces-default" - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckHasIAMRole(t, "workspaces_DefaultRole") }, Providers: testAccProviders, CheckDestroy: testAccCheckAwsWorkspacesDirectoryDestroy, Steps: []resource.TestStep{ { - Config: testAccWorkspacesDirectoryConfigA(booster), + Config: testAccWorkspacesDirectoryConfigA(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAwsWorkspacesDirectoryExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "subnet_ids.#", "2"), @@ -96,14 +82,14 @@ func testAccAwsWorkspacesDirectory_basic(t *testing.T) { resource.TestCheckResourceAttrPair(resourceName, "directory_name", directoryResourceName, "name"), resource.TestCheckResourceAttrPair(resourceName, "alias", directoryResourceName, "alias"), resource.TestCheckResourceAttrPair(resourceName, "directory_id", directoryResourceName, "id"), - resource.TestCheckResourceAttrPair(resourceName, "iam_role_id", iamRoleResourceName, "arn"), + resource.TestCheckResourceAttrPair(resourceName, "iam_role_id", iamRoleDataSourceName, "arn"), resource.TestCheckResourceAttrSet(resourceName, "workspace_security_group_id"), resource.TestCheckResourceAttrSet(resourceName, "registration_code"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { - Config: testAccWorkspacesDirectoryConfigB(booster), + Config: testAccWorkspacesDirectoryConfigB(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAwsWorkspacesDirectoryExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "self_service_permissions.#", "1"), @@ -115,7 +101,7 @@ func testAccAwsWorkspacesDirectory_basic(t *testing.T) { ), }, { - Config: testAccWorkspacesDirectoryConfigC(booster), + Config: testAccWorkspacesDirectoryConfigC(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAwsWorkspacesDirectoryExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "self_service_permissions.#", "1"), @@ -135,18 +121,19 @@ func testAccAwsWorkspacesDirectory_basic(t *testing.T) { }) } -func testAccAwsWorkspacesDirectory_disappears(t *testing.T) { +func TestAccAwsWorkspacesDirectory_disappears(t *testing.T) { var v workspaces.WorkspaceDirectory - booster := acctest.RandString(8) + rName := acctest.RandString(8) + resourceName := "aws_workspaces_directory.main" - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckHasIAMRole(t, "workspaces_DefaultRole") }, Providers: testAccProviders, CheckDestroy: testAccCheckAwsWorkspacesDirectoryDestroy, Steps: []resource.TestStep{ { - Config: testAccWorkspacesDirectoryConfigA(booster), + Config: testAccWorkspacesDirectoryConfigA(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAwsWorkspacesDirectoryExists(resourceName, &v), testAccCheckAwsWorkspacesDirectoryDisappears(&v), @@ -157,18 +144,19 @@ func testAccAwsWorkspacesDirectory_disappears(t *testing.T) { }) } -func testAccAwsWorkspacesDirectory_subnetIds(t *testing.T) { +func TestAccAwsWorkspacesDirectory_subnetIds(t *testing.T) { var v workspaces.WorkspaceDirectory - booster := acctest.RandString(8) + rName := acctest.RandString(8) + resourceName := "aws_workspaces_directory.main" - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckHasIAMRole(t, "workspaces_DefaultRole") }, Providers: testAccProviders, CheckDestroy: testAccCheckAwsWorkspacesDirectoryDestroy, Steps: []resource.TestStep{ { - Config: testAccWorkspacesDirectoryConfig_subnetIds(booster), + Config: testAccWorkspacesDirectoryConfig_subnetIds(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWorkspacesDirectoryExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "subnet_ids.#", "2"), @@ -183,13 +171,14 @@ func testAccAwsWorkspacesDirectory_subnetIds(t *testing.T) { }) } -func testAccAwsWorkspacesDirectory_tags(t *testing.T) { +func TestAccAwsWorkspacesDirectory_tags(t *testing.T) { var v workspaces.WorkspaceDirectory rName := acctest.RandString(8) + resourceName := "aws_workspaces_directory.main" - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckHasIAMRole(t, "workspaces_DefaultRole") }, Providers: testAccProviders, CheckDestroy: testAccCheckAwsWorkspacesDirectoryDestroy, Steps: []resource.TestStep{ @@ -227,6 +216,25 @@ func testAccAwsWorkspacesDirectory_tags(t *testing.T) { }) } +func testAccPreCheckHasIAMRole(t *testing.T, roleName string) { + conn := testAccProvider.Meta().(*AWSClient).iamconn + + input := &iam.GetRoleInput{ + RoleName: aws.String(roleName), + } + _, err := conn.GetRole(input) + + if isAWSErr(err, iam.ErrCodeNoSuchEntityException, "") { + t.Skipf("skipping acceptance test: required IAM role \"%s\" is not present", roleName) + } + if testAccPreCheckSkipError(err) { + t.Skipf("skipping acceptance test: %s", err) + } + if err != nil { + t.Fatalf("unexpected PreCheck error: %s", err) + } +} + func testAccCheckAwsWorkspacesDirectoryDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).workspacesconn @@ -368,7 +376,7 @@ func TestFlattenSelfServicePermissions(t *testing.T) { } // Extract common infra -func testAccAwsWorkspacesDirectoryConfig_Prerequisites(booster string) string { +func testAccAwsWorkspacesDirectoryConfig_Prerequisites(rName string) string { return fmt.Sprintf(` data "aws_region" "current" {} @@ -389,103 +397,81 @@ locals { workspaces_az_ids = lookup(local.region_workspaces_az_ids, data.aws_region.current.name, data.aws_availability_zones.available.zone_ids) } - resource "aws_vpc" "main" { - cidr_block = "10.0.0.0/16" +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" - tags = { - Name = "tf-testacc-workspaces-directory-%[1]s" - } - } + tags = { + Name = "tf-testacc-workspaces-directory-%[1]s" + } +} - resource "aws_subnet" "primary" { - vpc_id = "${aws_vpc.main.id}" - availability_zone_id = "${local.workspaces_az_ids[0]}" - cidr_block = "10.0.1.0/24" +resource "aws_subnet" "primary" { + vpc_id = "${aws_vpc.main.id}" + availability_zone_id = "${local.workspaces_az_ids[0]}" + cidr_block = "10.0.1.0/24" - tags = { - Name = "tf-testacc-workspaces-directory-%[1]s-primary" - } - } + tags = { + Name = "tf-testacc-workspaces-directory-%[1]s-primary" + } +} - resource "aws_subnet" "secondary" { - vpc_id = "${aws_vpc.main.id}" - availability_zone_id = "${local.workspaces_az_ids[1]}" - cidr_block = "10.0.2.0/24" +resource "aws_subnet" "secondary" { + vpc_id = "${aws_vpc.main.id}" + availability_zone_id = "${local.workspaces_az_ids[1]}" + cidr_block = "10.0.2.0/24" - tags = { - Name = "tf-testacc-workspaces-directory-%[1]s-secondary" - } - } + tags = { + Name = "tf-testacc-workspaces-directory-%[1]s-secondary" + } +} resource "aws_directory_service_directory" "main" { - size = "Small" - name = "tf-acctest.neverland.com" + size = "Small" + name = "tf-acctest.neverland.com" password = "#S1ncerely" vpc_settings { - vpc_id = "${aws_vpc.main.id}" - subnet_ids = ["${aws_subnet.primary.id}","${aws_subnet.secondary.id}"] + vpc_id = "${aws_vpc.main.id}" + subnet_ids = ["${aws_subnet.primary.id}", "${aws_subnet.secondary.id}"] } tags = { Name = "tf-testacc-workspaces-directory-%[1]s" } } - -data "aws_iam_policy_document" "workspaces" { - statement { - actions = ["sts:AssumeRole"] - - principals { - type = "Service" - identifiers = ["workspaces.amazonaws.com"] - } - } -} - -resource "aws_iam_role" "workspaces-default" { - name = "workspaces_DefaultRole" - assume_role_policy = data.aws_iam_policy_document.workspaces.json -} - -resource "aws_iam_role_policy_attachment" "workspaces-default-service-access" { - role = aws_iam_role.workspaces-default.name - policy_arn = "arn:aws:iam::aws:policy/AmazonWorkSpacesServiceAccess" -} - -resource "aws_iam_role_policy_attachment" "workspaces-default-self-service-access" { - role = aws_iam_role.workspaces-default.name - policy_arn = "arn:aws:iam::aws:policy/AmazonWorkSpacesSelfServiceAccess" -} -`, booster) +`, rName) } -func testAccWorkspacesDirectoryConfigA(booster string) string { - return testAccAwsWorkspacesDirectoryConfig_Prerequisites(booster) + fmt.Sprintf(` +func testAccWorkspacesDirectoryConfigA(rName string) string { + return testAccAwsWorkspacesDirectoryConfig_Prerequisites(rName) + fmt.Sprintf(` resource "aws_workspaces_directory" "main" { directory_id = "${aws_directory_service_directory.main.id}" } + +data "aws_iam_role" "workspaces-default" { + name = "workspaces_DefaultRole" +} `) } -func testAccWorkspacesDirectoryConfigB(booster string) string { - return testAccAwsWorkspacesDirectoryConfig_Prerequisites(booster) + fmt.Sprintf(` +func testAccWorkspacesDirectoryConfigB(rName string) string { + return testAccAwsWorkspacesDirectoryConfig_Prerequisites(rName) + fmt.Sprintf(` resource "aws_workspaces_directory" "main" { directory_id = "${aws_directory_service_directory.main.id}" self_service_permissions { - change_compute_type = false + change_compute_type = false increase_volume_size = true - rebuild_workspace = true - restart_workspace = false - switch_running_mode = true + rebuild_workspace = true + restart_workspace = false + switch_running_mode = true } } `) } -func testAccWorkspacesDirectoryConfigC(booster string) string { - return testAccAwsWorkspacesDirectoryConfig_Prerequisites(booster) + fmt.Sprintf(` +func testAccWorkspacesDirectoryConfigC(rName string) string { + return testAccAwsWorkspacesDirectoryConfig_Prerequisites(rName) + fmt.Sprintf(` resource "aws_workspaces_directory" "main" { directory_id = "${aws_directory_service_directory.main.id}" @@ -497,8 +483,8 @@ resource "aws_workspaces_directory" "main" { `) } -func testAccWorkspacesDirectoryConfig_subnetIds(booster string) string { - return testAccAwsWorkspacesDirectoryConfig_Prerequisites(booster) + fmt.Sprintf(` +func testAccWorkspacesDirectoryConfig_subnetIds(rName string) string { + return testAccAwsWorkspacesDirectoryConfig_Prerequisites(rName) + fmt.Sprintf(` resource "aws_workspaces_directory" "main" { directory_id = "${aws_directory_service_directory.main.id}" subnet_ids = ["${aws_subnet.primary.id}","${aws_subnet.secondary.id}"] diff --git a/aws/resource_aws_workspaces_workspace.go b/aws/resource_aws_workspaces_workspace.go index 34ab2eb4d09f..8ff5e7903a4d 100644 --- a/aws/resource_aws_workspaces_workspace.go +++ b/aws/resource_aws_workspaces_workspace.go @@ -3,15 +3,14 @@ package aws import ( "fmt" "log" - "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/workspaces" - "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "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" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/workspaces/waiter" ) func resourceAwsWorkspacesWorkspace() *schema.Resource { @@ -166,34 +165,20 @@ func resourceAwsWorkspacesWorkspaceCreate(d *schema.ResourceData, meta interface } wsFail := resp.FailedRequests - if len(wsFail) > 0 { return fmt.Errorf("workspace creation failed: %s", *wsFail[0].ErrorMessage) } - wsPendingID := aws.StringValue(resp.PendingRequests[0].WorkspaceId) + workspaceID := aws.StringValue(resp.PendingRequests[0].WorkspaceId) - log.Printf("[DEBUG] Waiting for workspace to be available...") - stateConf := &resource.StateChangeConf{ - Pending: []string{ - workspaces.WorkspaceStatePending, - workspaces.WorkspaceStateStarting, - }, - Target: []string{workspaces.WorkspaceStateAvailable}, - Refresh: workspaceRefreshStateFunc(conn, wsPendingID), - PollInterval: 30 * time.Second, - Timeout: 25 * time.Minute, - } - - _, err = stateConf.WaitForState() + log.Printf("[DEBUG] Waiting for workspace %q to be available...", workspaceID) + _, err = waiter.WorkspaceAvailable(conn, workspaceID) if err != nil { - return fmt.Errorf("workspace %q is not available: %s", wsPendingID, err) + return fmt.Errorf("workspace %q is not available: %s", workspaceID, err) } - wsAvailableID := wsPendingID - - d.SetId(wsAvailableID) - log.Printf("[DEBUG] Workspace %q is available", d.Id()) + d.SetId(workspaceID) + log.Printf("[DEBUG] Workspace %q is available", workspaceID) return resourceAwsWorkspacesWorkspaceRead(d, meta) } @@ -202,7 +187,7 @@ func resourceAwsWorkspacesWorkspaceRead(d *schema.ResourceData, meta interface{} conn := meta.(*AWSClient).workspacesconn ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - workspace, state, err := workspaceRefreshStateFunc(conn, d.Id())() + rawOutput, state, err := waiter.WorkspaceState(conn, d.Id())() if err != nil { return fmt.Errorf("error reading workspace (%s): %s", d.Id(), err) } @@ -212,17 +197,17 @@ func resourceAwsWorkspacesWorkspaceRead(d *schema.ResourceData, meta interface{} return nil } - ws := workspace.(*workspaces.Workspace) - d.Set("bundle_id", ws.BundleId) - d.Set("directory_id", ws.DirectoryId) - d.Set("ip_address", ws.IpAddress) - d.Set("computer_name", ws.ComputerName) - d.Set("state", ws.State) - d.Set("root_volume_encryption_enabled", ws.RootVolumeEncryptionEnabled) - d.Set("user_name", ws.UserName) - d.Set("user_volume_encryption_enabled", ws.UserVolumeEncryptionEnabled) - d.Set("volume_encryption_key", ws.VolumeEncryptionKey) - if err := d.Set("workspace_properties", flattenWorkspaceProperties(ws.WorkspaceProperties)); err != nil { + workspace := rawOutput.(*workspaces.Workspace) + d.Set("bundle_id", workspace.BundleId) + d.Set("directory_id", workspace.DirectoryId) + d.Set("ip_address", workspace.IpAddress) + d.Set("computer_name", workspace.ComputerName) + d.Set("state", workspace.State) + d.Set("root_volume_encryption_enabled", workspace.RootVolumeEncryptionEnabled) + d.Set("user_name", workspace.UserName) + d.Set("user_volume_encryption_enabled", workspace.UserVolumeEncryptionEnabled) + d.Set("volume_encryption_key", workspace.VolumeEncryptionKey) + if err := d.Set("workspace_properties", flattenWorkspaceProperties(workspace.WorkspaceProperties)); err != nil { return fmt.Errorf("error setting workspace properties: %s", err) } @@ -310,34 +295,7 @@ func workspaceDelete(id string, conn *workspaces.WorkSpaces) error { } log.Printf("[DEBUG] Waiting for workspace %q to be terminated", id) - stateConf := &resource.StateChangeConf{ - Pending: []string{ - workspaces.WorkspaceStatePending, - workspaces.WorkspaceStateAvailable, - workspaces.WorkspaceStateImpaired, - workspaces.WorkspaceStateUnhealthy, - workspaces.WorkspaceStateRebooting, - workspaces.WorkspaceStateStarting, - workspaces.WorkspaceStateRebuilding, - workspaces.WorkspaceStateRestoring, - workspaces.WorkspaceStateMaintenance, - workspaces.WorkspaceStateAdminMaintenance, - workspaces.WorkspaceStateSuspended, - workspaces.WorkspaceStateUpdating, - workspaces.WorkspaceStateStopping, - workspaces.WorkspaceStateStopped, - workspaces.WorkspaceStateTerminating, - workspaces.WorkspaceStateError, - }, - Target: []string{ - workspaces.WorkspaceStateTerminated, - }, - Refresh: workspaceRefreshStateFunc(conn, id), - PollInterval: 15 * time.Second, - Timeout: 10 * time.Minute, - } - - _, err = stateConf.WaitForState() + _, err = waiter.WorkspaceTerminated(conn, id) if err != nil { return fmt.Errorf("workspace %q was not terminated: %s", id, err) } @@ -386,52 +344,19 @@ func workspacePropertyUpdate(p string, conn *workspaces.WorkSpaces, d *schema.Re WorkspaceProperties: wsp, }) if err != nil { - return fmt.Errorf("workspace %q %s property was not modified: %s", d.Id(), p, err) + return fmt.Errorf("workspace %q %s property was not modified: %w", d.Id(), p, err) } log.Printf("[DEBUG] Waiting for workspace %q %s property to be modified...", d.Id(), p) - // OperationInProgressException: The properties of this WorkSpace are currently under modification. Please try again in a moment. - // AWS Workspaces service doesn't change instance status to "Updating" during property modification. Respective AWS Support feature request has been created. Meanwhile, artificial delay is placed here as a workaround. - time.Sleep(1 * time.Minute) - - stateConf := &resource.StateChangeConf{ - Pending: []string{ - workspaces.WorkspaceStateUpdating, - }, - Target: []string{ - workspaces.WorkspaceStateAvailable, - workspaces.WorkspaceStateStopped, - }, - Refresh: workspaceRefreshStateFunc(conn, d.Id()), - PollInterval: 30 * time.Second, - Timeout: 10 * time.Minute, - } - - _, err = stateConf.WaitForState() + _, err = waiter.WorkspaceUpdated(conn, d.Id()) if err != nil { - return fmt.Errorf("workspace %q %s property was not modified: %s", d.Id(), p, err) + return fmt.Errorf("error modifying workspace %q property %q was not modified: %w", d.Id(), p, err) } log.Printf("[DEBUG] Workspace %q %s property is modified", d.Id(), p) return nil } -func workspaceRefreshStateFunc(conn *workspaces.WorkSpaces, workspaceID string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - resp, err := conn.DescribeWorkspaces(&workspaces.DescribeWorkspacesInput{ - WorkspaceIds: []*string{aws.String(workspaceID)}, - }) - if err != nil { - return nil, workspaces.WorkspaceStateError, err - } - if len(resp.Workspaces) == 0 { - return nil, workspaces.WorkspaceStateTerminated, nil - } - workspace := resp.Workspaces[0] - return workspace, aws.StringValue(workspace.State), nil - } -} - func expandWorkspaceProperties(properties []interface{}) *workspaces.WorkspaceProperties { log.Printf("[DEBUG] Expand Workspace properties: %+v ", properties) diff --git a/aws/resource_aws_workspaces_workspace_test.go b/aws/resource_aws_workspaces_workspace_test.go index e4adfb38573a..ca41d084cb31 100644 --- a/aws/resource_aws_workspaces_workspace_test.go +++ b/aws/resource_aws_workspaces_workspace_test.go @@ -10,6 +10,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/workspaces" multierror "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/terraform" ) @@ -52,20 +53,23 @@ func testSweepWorkspaces(region string) error { } func TestAccAwsWorkspacesWorkspace_basic(t *testing.T) { + var v workspaces.Workspace + rName := acctest.RandString(8) + + resourceName := "aws_workspaces_workspace.test" directoryResourceName := "aws_workspaces_directory.test" bundleDataSourceName := "data.aws_workspaces_bundle.test" - resourceName := "aws_workspaces_workspace.test" - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckHasIAMRole(t, "workspaces_DefaultRole") }, Providers: testAccProviders, CheckDestroy: testAccCheckAwsWorkspacesWorkspaceDestroy, Steps: []resource.TestStep{ { Destroy: false, - Config: testAccWorkspacesWorkspaceConfig(), + Config: testAccWorkspacesWorkspaceConfig(rName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsWorkspacesWorkspaceExists(resourceName), + testAccCheckAwsWorkspacesWorkspaceExists(resourceName, &v), resource.TestCheckResourceAttrPair(resourceName, "directory_id", directoryResourceName, "id"), resource.TestCheckResourceAttrPair(resourceName, "bundle_id", bundleDataSourceName, "id"), resource.TestMatchResourceAttr(resourceName, "ip_address", regexp.MustCompile(`\d+\.\d+\.\d+\.\d+`)), @@ -91,18 +95,21 @@ func TestAccAwsWorkspacesWorkspace_basic(t *testing.T) { }) } -func TestAccAwsWorkspacesWorkspace_Tags(t *testing.T) { +func TestAccAwsWorkspacesWorkspace_tags(t *testing.T) { + var v1, v2, v3 workspaces.Workspace + rName := acctest.RandString(8) + resourceName := "aws_workspaces_workspace.test" - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckHasIAMRole(t, "workspaces_DefaultRole") }, Providers: testAccProviders, CheckDestroy: testAccCheckAwsWorkspacesWorkspaceDestroy, Steps: []resource.TestStep{ { - Config: testAccWorkspacesWorkspaceConfig_TagsA(), + Config: testAccWorkspacesWorkspaceConfig_TagsA(rName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsWorkspacesWorkspaceExists(resourceName), + testAccCheckAwsWorkspacesWorkspaceExists(resourceName, &v1), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), resource.TestCheckResourceAttr(resourceName, "tags.TerraformProviderAwsTest", "true"), resource.TestCheckResourceAttr(resourceName, "tags.Alpha", "1"), @@ -114,18 +121,18 @@ func TestAccAwsWorkspacesWorkspace_Tags(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccWorkspacesWorkspaceConfig_TagsB(), + Config: testAccWorkspacesWorkspaceConfig_TagsB(rName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsWorkspacesWorkspaceExists(resourceName), + testAccCheckAwsWorkspacesWorkspaceExists(resourceName, &v2), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), resource.TestCheckResourceAttr(resourceName, "tags.TerraformProviderAwsTest", "true"), resource.TestCheckResourceAttr(resourceName, "tags.Beta", "2"), ), }, { - Config: testAccWorkspacesWorkspaceConfig_TagsC(), + Config: testAccWorkspacesWorkspaceConfig_TagsC(rName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsWorkspacesWorkspaceExists(resourceName), + testAccCheckAwsWorkspacesWorkspaceExists(resourceName, &v3), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.TerraformProviderAwsTest", "true"), ), @@ -134,19 +141,22 @@ func TestAccAwsWorkspacesWorkspace_Tags(t *testing.T) { }) } -func TestAccAwsWorkspacesWorkspace_WorkspaceProperties(t *testing.T) { +func TestAccAwsWorkspacesWorkspace_workspaceProperties(t *testing.T) { + var v1, v2, v3 workspaces.Workspace + rName := acctest.RandString(8) + resourceName := "aws_workspaces_workspace.test" - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckHasIAMRole(t, "workspaces_DefaultRole") }, Providers: testAccProviders, CheckDestroy: testAccCheckAwsWorkspacesWorkspaceDestroy, Steps: []resource.TestStep{ { Destroy: false, - Config: testAccWorkspacesWorkspaceConfig_WorkspacePropertiesA(), + Config: testAccWorkspacesWorkspaceConfig_WorkspacePropertiesA(rName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsWorkspacesWorkspaceExists(resourceName), + testAccCheckAwsWorkspacesWorkspaceExists(resourceName, &v1), resource.TestCheckResourceAttr(resourceName, "workspace_properties.#", "1"), resource.TestCheckResourceAttr(resourceName, "workspace_properties.0.compute_type_name", workspaces.ComputeValue), resource.TestCheckResourceAttr(resourceName, "workspace_properties.0.root_volume_size_gib", "80"), @@ -161,9 +171,9 @@ func TestAccAwsWorkspacesWorkspace_WorkspaceProperties(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccWorkspacesWorkspaceConfig_WorkspacePropertiesB(), + Config: testAccWorkspacesWorkspaceConfig_WorkspacePropertiesB(rName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsWorkspacesWorkspaceExists(resourceName), + testAccCheckAwsWorkspacesWorkspaceExists(resourceName, &v2), resource.TestCheckResourceAttr(resourceName, "workspace_properties.#", "1"), resource.TestCheckResourceAttr(resourceName, "workspace_properties.0.compute_type_name", workspaces.ComputeValue), resource.TestCheckResourceAttr(resourceName, "workspace_properties.0.root_volume_size_gib", "80"), @@ -173,9 +183,9 @@ func TestAccAwsWorkspacesWorkspace_WorkspaceProperties(t *testing.T) { ), }, { - Config: testAccWorkspacesWorkspaceConfig_WorkspacePropertiesC(), + Config: testAccWorkspacesWorkspaceConfig_WorkspacePropertiesC(rName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsWorkspacesWorkspaceExists(resourceName), + testAccCheckAwsWorkspacesWorkspaceExists(resourceName, &v3), resource.TestCheckResourceAttr(resourceName, "workspace_properties.#", "1"), resource.TestCheckResourceAttr(resourceName, "workspace_properties.0.compute_type_name", workspaces.ComputeValue), resource.TestCheckResourceAttr(resourceName, "workspace_properties.0.root_volume_size_gib", "80"), @@ -189,13 +199,15 @@ func TestAccAwsWorkspacesWorkspace_WorkspaceProperties(t *testing.T) { } func TestAccAwsWorkspacesWorkspace_validateRootVolumeSize(t *testing.T) { - resource.Test(t, resource.TestCase{ + rName := acctest.RandString(8) + + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckAwsWorkspacesWorkspaceDestroy, Steps: []resource.TestStep{ { - Config: testAccWorkspacesWorkspaceConfig_validateRootVolumeSize(), + Config: testAccWorkspacesWorkspaceConfig_validateRootVolumeSize(rName), ExpectError: regexp.MustCompile(regexp.QuoteMeta("expected workspace_properties.0.root_volume_size_gib to be one of [80], got 90")), }, }, @@ -203,13 +215,15 @@ func TestAccAwsWorkspacesWorkspace_validateRootVolumeSize(t *testing.T) { } func TestAccAwsWorkspacesWorkspace_validateUserVolumeSize(t *testing.T) { - resource.Test(t, resource.TestCase{ + rName := acctest.RandString(8) + + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckAwsWorkspacesWorkspaceDestroy, Steps: []resource.TestStep{ { - Config: testAccWorkspacesWorkspaceConfig_validateUserVolumeSize(), + Config: testAccWorkspacesWorkspaceConfig_validateUserVolumeSize(rName), ExpectError: regexp.MustCompile(regexp.QuoteMeta("workspace_properties.0.user_volume_size_gib to be one of [10 50], got 60")), }, }, @@ -244,7 +258,7 @@ func testAccCheckAwsWorkspacesWorkspaceDestroy(s *terraform.State) error { return nil } -func testAccCheckAwsWorkspacesWorkspaceExists(n string) resource.TestCheckFunc { +func testAccCheckAwsWorkspacesWorkspaceExists(n string, v *workspaces.Workspace) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -253,33 +267,38 @@ func testAccCheckAwsWorkspacesWorkspaceExists(n string) resource.TestCheckFunc { conn := testAccProvider.Meta().(*AWSClient).workspacesconn - _, err := conn.DescribeWorkspaces(&workspaces.DescribeWorkspacesInput{ + output, err := conn.DescribeWorkspaces(&workspaces.DescribeWorkspacesInput{ WorkspaceIds: []*string{aws.String(rs.Primary.ID)}, }) if err != nil { return err } - return nil + if *output.Workspaces[0].WorkspaceId == rs.Primary.ID { + *v = *output.Workspaces[0] + return nil + } + + return fmt.Errorf("workspace %q not found", rs.Primary.ID) } } -func testAccAwsWorkspacesWorkspaceConfig_Prerequisites() string { +func testAccAwsWorkspacesWorkspaceConfig_Prerequisites(rName string) string { return composeConfig( - testAccAwsWorkspacesDirectoryConfig_Prerequisites(""), + testAccAwsWorkspacesDirectoryConfig_Prerequisites(rName), fmt.Sprintf(` data "aws_workspaces_bundle" "test" { bundle_id = "wsb-bh8rsxt14" # Value with Windows 10 (English) } resource "aws_workspaces_directory" "test" { - directory_id = "${aws_directory_service_directory.main.id}" + directory_id = "${aws_directory_service_directory.main.id}" } `)) } -func testAccWorkspacesWorkspaceConfig() string { - return testAccAwsWorkspacesWorkspaceConfig_Prerequisites() + fmt.Sprintf(` +func testAccWorkspacesWorkspaceConfig(rName string) string { + return testAccAwsWorkspacesWorkspaceConfig_Prerequisites(rName) + fmt.Sprintf(` resource "aws_workspaces_workspace" "test" { bundle_id = "${data.aws_workspaces_bundle.test.id}" directory_id = "${aws_workspaces_directory.test.id}" @@ -287,18 +306,12 @@ resource "aws_workspaces_workspace" "test" { # NOTE: WorkSpaces API doesn't allow creating users in the directory. # However, "Administrator"" user is always present in a bare directory. user_name = "Administrator" - - depends_on = [ - # The role "workspaces_DefaultRole" requires the policy arn:aws:iam::aws:policy/AmazonWorkSpacesServiceAccess - # to create and delete the ENI that the Workspaces service creates for the Workspace - aws_iam_role_policy_attachment.workspaces-default-service-access, - ] } `) } -func testAccWorkspacesWorkspaceConfig_TagsA() string { - return testAccAwsWorkspacesWorkspaceConfig_Prerequisites() + fmt.Sprintf(` +func testAccWorkspacesWorkspaceConfig_TagsA(rName string) string { + return testAccAwsWorkspacesWorkspaceConfig_Prerequisites(rName) + fmt.Sprintf(` resource "aws_workspaces_workspace" "test" { bundle_id = "${data.aws_workspaces_bundle.test.id}" directory_id = "${aws_workspaces_directory.test.id}" @@ -311,18 +324,12 @@ resource "aws_workspaces_workspace" "test" { TerraformProviderAwsTest = true Alpha = 1 } - - depends_on = [ - # The role "workspaces_DefaultRole" requires the policy arn:aws:iam::aws:policy/AmazonWorkSpacesServiceAccess - # to create and delete the ENI that the Workspaces service creates for the Workspace - aws_iam_role_policy_attachment.workspaces-default-service-access, - ] } `) } -func testAccWorkspacesWorkspaceConfig_TagsB() string { - return testAccAwsWorkspacesWorkspaceConfig_Prerequisites() + fmt.Sprintf(` +func testAccWorkspacesWorkspaceConfig_TagsB(rName string) string { + return testAccAwsWorkspacesWorkspaceConfig_Prerequisites(rName) + fmt.Sprintf(` resource "aws_workspaces_workspace" "test" { bundle_id = "${data.aws_workspaces_bundle.test.id}" directory_id = "${aws_workspaces_directory.test.id}" @@ -335,18 +342,12 @@ resource "aws_workspaces_workspace" "test" { TerraformProviderAwsTest = true Beta = 2 } - - depends_on = [ - # The role "workspaces_DefaultRole" requires the policy arn:aws:iam::aws:policy/AmazonWorkSpacesServiceAccess - # to create and delete the ENI that the Workspaces service creates for the Workspace - aws_iam_role_policy_attachment.workspaces-default-service-access, - ] } `) } -func testAccWorkspacesWorkspaceConfig_TagsC() string { - return testAccAwsWorkspacesWorkspaceConfig_Prerequisites() + fmt.Sprintf(` +func testAccWorkspacesWorkspaceConfig_TagsC(rName string) string { + return testAccAwsWorkspacesWorkspaceConfig_Prerequisites(rName) + fmt.Sprintf(` resource "aws_workspaces_workspace" "test" { bundle_id = "${data.aws_workspaces_bundle.test.id}" directory_id = "${aws_workspaces_directory.test.id}" @@ -358,18 +359,12 @@ resource "aws_workspaces_workspace" "test" { tags = { TerraformProviderAwsTest = true } - - depends_on = [ - # The role "workspaces_DefaultRole" requires the policy arn:aws:iam::aws:policy/AmazonWorkSpacesServiceAccess - # to create and delete the ENI that the Workspaces service creates for the Workspace - aws_iam_role_policy_attachment.workspaces-default-service-access, - ] } `) } -func testAccWorkspacesWorkspaceConfig_WorkspacePropertiesA() string { - return testAccAwsWorkspacesWorkspaceConfig_Prerequisites() + fmt.Sprintf(` +func testAccWorkspacesWorkspaceConfig_WorkspacePropertiesA(rName string) string { + return testAccAwsWorkspacesWorkspaceConfig_Prerequisites(rName) + fmt.Sprintf(` resource "aws_workspaces_workspace" "test" { bundle_id = "${data.aws_workspaces_bundle.test.id}" directory_id = "${aws_workspaces_directory.test.id}" @@ -387,18 +382,12 @@ resource "aws_workspaces_workspace" "test" { tags = { TerraformProviderAwsTest = true } - - depends_on = [ - # The role "workspaces_DefaultRole" requires the policy arn:aws:iam::aws:policy/AmazonWorkSpacesServiceAccess - # to create and delete the ENI that the Workspaces service creates for the Workspace - aws_iam_role_policy_attachment.workspaces-default-service-access, - ] } `) } -func testAccWorkspacesWorkspaceConfig_WorkspacePropertiesB() string { - return testAccAwsWorkspacesWorkspaceConfig_Prerequisites() + fmt.Sprintf(` +func testAccWorkspacesWorkspaceConfig_WorkspacePropertiesB(rName string) string { + return testAccAwsWorkspacesWorkspaceConfig_Prerequisites(rName) + fmt.Sprintf(` resource "aws_workspaces_workspace" "test" { bundle_id = "${data.aws_workspaces_bundle.test.id}" directory_id = "${aws_workspaces_directory.test.id}" @@ -415,18 +404,12 @@ resource "aws_workspaces_workspace" "test" { tags = { TerraformProviderAwsTest = true } - - depends_on = [ - # The role "workspaces_DefaultRole" requires the policy arn:aws:iam::aws:policy/AmazonWorkSpacesServiceAccess - # to create and delete the ENI that the Workspaces service creates for the Workspace - aws_iam_role_policy_attachment.workspaces-default-service-access, - ] } `) } -func testAccWorkspacesWorkspaceConfig_WorkspacePropertiesC() string { - return testAccAwsWorkspacesWorkspaceConfig_Prerequisites() + fmt.Sprintf(` +func testAccWorkspacesWorkspaceConfig_WorkspacePropertiesC(rName string) string { + return testAccAwsWorkspacesWorkspaceConfig_Prerequisites(rName) + fmt.Sprintf(` resource "aws_workspaces_workspace" "test" { bundle_id = "${data.aws_workspaces_bundle.test.id}" directory_id = "${aws_workspaces_directory.test.id}" @@ -437,18 +420,12 @@ resource "aws_workspaces_workspace" "test" { workspace_properties { } - - depends_on = [ - # The role "workspaces_DefaultRole" requires the policy arn:aws:iam::aws:policy/AmazonWorkSpacesServiceAccess - # to create and delete the ENI that the Workspaces service creates for the Workspace - aws_iam_role_policy_attachment.workspaces-default-service-access, - ] } `) } -func testAccWorkspacesWorkspaceConfig_validateRootVolumeSize() string { - return testAccAwsWorkspacesWorkspaceConfig_Prerequisites() + fmt.Sprintf(` +func testAccWorkspacesWorkspaceConfig_validateRootVolumeSize(rName string) string { + return testAccAwsWorkspacesWorkspaceConfig_Prerequisites(rName) + fmt.Sprintf(` resource "aws_workspaces_workspace" "test" { bundle_id = "${data.aws_workspaces_bundle.test.id}" directory_id = "${aws_workspaces_directory.test.id}" @@ -465,18 +442,12 @@ resource "aws_workspaces_workspace" "test" { tags = { TerraformProviderAwsTest = true } - - depends_on = [ - # The role "workspaces_DefaultRole" requires the policy arn:aws:iam::aws:policy/AmazonWorkSpacesServiceAccess - # to create and delete the ENI that the Workspaces service creates for the Workspace - aws_iam_role_policy_attachment.workspaces-default-service-access, - ] } `) } -func testAccWorkspacesWorkspaceConfig_validateUserVolumeSize() string { - return testAccAwsWorkspacesWorkspaceConfig_Prerequisites() + fmt.Sprintf(` +func testAccWorkspacesWorkspaceConfig_validateUserVolumeSize(rName string) string { + return testAccAwsWorkspacesWorkspaceConfig_Prerequisites(rName) + fmt.Sprintf(` resource "aws_workspaces_workspace" "test" { bundle_id = "${data.aws_workspaces_bundle.test.id}" directory_id = "${aws_workspaces_directory.test.id}" @@ -493,12 +464,6 @@ resource "aws_workspaces_workspace" "test" { tags = { TerraformProviderAwsTest = true } - - depends_on = [ - # The role "workspaces_DefaultRole" requires the policy arn:aws:iam::aws:policy/AmazonWorkSpacesServiceAccess - # to create and delete the ENI that the Workspaces service creates for the Workspace - aws_iam_role_policy_attachment.workspaces-default-service-access, - ] } `) }