From 4004166de8f8f0dd60c60aba55cf553966dcf538 Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Thu, 5 Aug 2021 18:10:53 -0600 Subject: [PATCH 01/28] feat: added resource for appstream fleet and tests docs --- aws/provider.go | 1 + aws/resource_aws_appstream_fleet.go | 632 +++++++++++++++++++ aws/resource_aws_appstream_fleet_test.go | 195 ++++++ aws/resource_aws_appstream_test.go | 27 + website/docs/r/appstream_fleet.html.markdown | 73 +++ 5 files changed, 928 insertions(+) create mode 100644 aws/resource_aws_appstream_fleet.go create mode 100644 aws/resource_aws_appstream_fleet_test.go create mode 100644 aws/resource_aws_appstream_test.go create mode 100644 website/docs/r/appstream_fleet.html.markdown diff --git a/aws/provider.go b/aws/provider.go index 8ca33fb7ae8..62b74cf88a8 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -537,6 +537,7 @@ func Provider() *schema.Provider { "aws_apprunner_custom_domain_association": resourceAwsAppRunnerCustomDomainAssociation(), "aws_apprunner_service": resourceAwsAppRunnerService(), "aws_appstream_stack": resourceAwsAppStreamStack(), + "aws_appstream_fleet": resourceAwsAppstreamFleet(), "aws_appsync_api_key": resourceAwsAppsyncApiKey(), "aws_appsync_datasource": resourceAwsAppsyncDatasource(), "aws_appsync_function": resourceAwsAppsyncFunction(), diff --git a/aws/resource_aws_appstream_fleet.go b/aws/resource_aws_appstream_fleet.go new file mode 100644 index 00000000000..12821e0c919 --- /dev/null +++ b/aws/resource_aws_appstream_fleet.go @@ -0,0 +1,632 @@ +package aws + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/appstream" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "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" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" +) + +func resourceAwsAppstreamFleet() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceAwsAppstreamFleetCreate, + ReadWithoutTimeout: resourceAwsAppstreamFleetRead, + UpdateWithoutTimeout: resourceAwsAppstreamFleetUpdate, + DeleteWithoutTimeout: resourceAwsAppstreamFleetDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "compute_capacity": { + Type: schema.TypeList, + MaxItems: 1, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "desired_instances": { + Type: schema.TypeInt, + Required: true, + }, + }, + }, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringLenBetween(0, 256), + }, + "disconnect_timeout_in_seconds": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntBetween(60, 360000), + }, + "display_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringLenBetween(0, 100), + }, + "domain_join_info": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "directory_name": { + Type: schema.TypeString, + Optional: true, + }, + "organizational_unit_distinguished_name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "enable_default_internet_access": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + "fleet_type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(appstream.FleetType_Values(), false), + }, + "iam_role_arn": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateArn, + }, + "idle_disconnect_timeout_in_seconds": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + ValidateFunc: validation.IntBetween(60, 3600), + }, + "image_arn": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "image_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "instance_type": { + Type: schema.TypeString, + Required: true, + }, + "max_user_duration_in_seconds": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntBetween(600, 360000), + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"name_prefix"}, + }, + "name_prefix": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"name"}, + }, + "stream_view": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice(appstream.StreamView_Values(), false), + }, + "stack_name": { + Type: schema.TypeString, + Optional: true, + }, + "state": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{appstream.FleetStateRunning, appstream.FleetStateStopped}, false), + }, + "vpc_config": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "security_group_ids": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "subnet_ids": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "tags": tagsSchema(), + "tags_all": tagsSchemaComputed(), + }, + } +} + +func resourceAwsAppstreamFleetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).appstreamconn + input := &appstream.CreateFleetInput{ + Name: aws.String(naming.Generate(d.Get("name").(string), d.Get("name_prefix").(string))), + } + + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) + + if v, ok := d.GetOk("compute_capacity"); ok { + input.ComputeCapacity = expandComputeCapacity(v.([]interface{})) + } + + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) + } + + if v, ok := d.GetOk("disconnect_timeout_in_seconds"); ok { + input.DisconnectTimeoutInSeconds = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("display_name"); ok { + input.DisplayName = aws.String(v.(string)) + } + + if v, ok := d.GetOk("domain_join_info"); ok { + input.DomainJoinInfo = expandDomainJoinInfo(v.([]interface{})) + } + + if v, ok := d.GetOk("enable_default_internet_access"); ok { + input.EnableDefaultInternetAccess = aws.Bool(v.(bool)) + } + + if v, ok := d.GetOk("fleet_type"); ok { + input.FleetType = aws.String(v.(string)) + } + + if v, ok := d.GetOk("image_name"); ok { + input.ImageName = aws.String(v.(string)) + } + + if v, ok := d.GetOk("instance_type"); ok { + input.InstanceType = aws.String(v.(string)) + } + + if v, ok := d.GetOk("iam_role_arn"); ok { + input.IamRoleArn = aws.String(v.(string)) + } + + if v, ok := d.GetOk("max_user_duration_in_seconds"); ok { + input.MaxUserDurationInSeconds = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("vpc_config"); ok { + input.VpcConfig = expandVpcConfig(v.([]interface{})) + } + + if len(tags) > 0 { + input.Tags = tags.IgnoreAws().AppstreamTags() + } + + resp, err := conn.CreateFleetWithContext(ctx, input) + if err != nil { + return diag.FromErr(fmt.Errorf("error creating Appstream Fleet (%s): %w", d.Id(), err)) + } + + if v, ok := d.GetOk("stack_name"); ok { + associateInput := &appstream.AssociateFleetInput{} + associateInput.FleetName = input.Name + associateInput.StackName = aws.String(v.(string)) + resp, err := conn.AssociateFleet(associateInput) + if err != nil { + return diag.FromErr(fmt.Errorf("error associating Appstream Fleet (%s): %w", d.Id(), err)) + } + + log.Printf("[DEBUG] %s", resp) + } + + if v, ok := d.GetOk("state"); ok { + if v == "RUNNING" { + desiredState := v + _, err := conn.StartFleetWithContext(ctx, &appstream.StartFleetInput{ + Name: resp.Fleet.Name, + }) + + if err != nil { + return diag.FromErr(fmt.Errorf("error starting Appstream Fleet (%s): %w", d.Id(), err)) + } + for { + resp, err := conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{ + Names: aws.StringSlice([]string{*input.Name}), + }) + if err != nil { + return diag.FromErr(fmt.Errorf("error describing Appstream Fleet (%s): %w", d.Id(), err)) + } + + currentState := resp.Fleets[0].State + if aws.StringValue(currentState) == desiredState { + break + } + if aws.StringValue(currentState) != desiredState { + time.Sleep(20 * time.Second) + continue + } + + } + } + } + + d.SetId(aws.StringValue(resp.Fleet.Name)) + + return resourceAwsAppstreamFleetRead(ctx, d, meta) +} + +func resourceAwsAppstreamFleetRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).appstreamconn + + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + resp, err := conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{Names: []*string{aws.String(d.Id())}}) + + if err != nil { + return diag.FromErr(fmt.Errorf("error reading Appstream Fleet (%s): %w", d.Id(), err)) + } + for _, v := range resp.Fleets { + d.Set("name", v.Name) + d.Set("name_prefix", naming.NamePrefixFromName(aws.StringValue(v.Name))) + + if err = d.Set("compute_capacity", flattenComputeCapacity(v.ComputeCapacityStatus)); err != nil { + return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Fleet (%s): %w", "compute_capacity", d.Id(), err)) + } + if err = d.Set("domain_join_info", flattenDomainInfo(v.DomainJoinInfo)); err != nil { + return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Fleet (%s): %w", "domain_join_info", d.Id(), err)) + } + + d.Set("description", v.Description) + d.Set("display_name", v.DisplayName) + d.Set("disconnect_timeout_in_seconds", v.DisconnectTimeoutInSeconds) + d.Set("idle_disconnect_timeout_in_seconds", v.IdleDisconnectTimeoutInSeconds) + d.Set("enable_default_internet_access", v.EnableDefaultInternetAccess) + d.Set("fleet_type", v.FleetType) + d.Set("image_name", v.ImageName) + d.Set("image_arn", v.ImageArn) + d.Set("iam_role_arn", v.IamRoleArn) + d.Set("stream_view", v.StreamView) + d.Set("arn", v.Arn) + + d.Set("instance_type", v.InstanceType) + d.Set("max_user_duration_in_seconds", v.MaxUserDurationInSeconds) + if err = d.Set("vpc_config", flattenVpcConfig(v.VpcConfig)); err != nil { + return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Fleet (%s): %w", "vpc_config", d.Id(), err)) + } + + d.Set("state", v.State) + + tg, err := conn.ListTagsForResource(&appstream.ListTagsForResourceInput{ + ResourceArn: v.Arn, + }) + if err != nil { + return diag.FromErr(fmt.Errorf("error listing stack tags for AppStream Stack (%s): %w", d.Id(), err)) + } + if tg.Tags == nil { + log.Printf("[DEBUG] Apsstream Stack tags (%s) not found", d.Id()) + return nil + } + tags := keyvaluetags.AppstreamKeyValueTags(tg.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig) + + if err = d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Stack (%s): %w", "tags", d.Id(), err)) + } + + if err = d.Set("tags_all", tags.Map()); err != nil { + return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Stack (%s): %w", "tags_all", d.Id(), err)) + } + + return nil + } + return nil +} + +func resourceAwsAppstreamFleetUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + + conn := meta.(*AWSClient).appstreamconn + input := &appstream.UpdateFleetInput{ + Name: aws.String(naming.Generate(d.Get("name").(string), d.Get("name_prefix").(string))), + } + + if d.HasChange("description") { + input.Description = aws.String(d.Get("description").(string)) + } + + if d.HasChange("compute_capacity") { + input.ComputeCapacity = expandComputeCapacity(d.Get("compute_capacity").([]interface{})) + } + + if d.HasChange("domain_join_info") { + input.DomainJoinInfo = expandDomainJoinInfo(d.Get("domain_join_info").([]interface{})) + } + + if d.HasChange("disconnect_timeout_in_seconds") { + input.DisconnectTimeoutInSeconds = aws.Int64(int64(d.Get("disconnect_timeout_in_seconds").(int))) + } + + if d.HasChange("idle_disconnect_timeout_in_seconds") { + input.IdleDisconnectTimeoutInSeconds = aws.Int64(int64(d.Get("idle_disconnect_timeout_in_seconds").(int))) + } + + if d.HasChange("display_name") { + input.DisplayName = aws.String(d.Get("display_name").(string)) + } + + if d.HasChange("image_name") { + input.ImageName = aws.String(d.Get("image_name").(string)) + } + + if d.HasChange("image_arn") { + input.ImageArn = aws.String(d.Get("image_arn").(string)) + } + + if d.HasChange("iam_role_arn") { + input.IamRoleArn = aws.String(d.Get("iam_role_arn").(string)) + } + + if d.HasChange("stream_view") { + input.StreamView = aws.String(d.Get("stream_view").(string)) + } + + if d.HasChange("instance_type") { + input.InstanceType = aws.String(d.Get("instance_type").(string)) + } + + if d.HasChange("max_user_duration_in_seconds") { + input.MaxUserDurationInSeconds = aws.Int64(int64(d.Get("max_user_duration_in_seconds").(int))) + } + + if d.HasChange("vpc_config") { + input.VpcConfig = expandVpcConfig(d.Get("vpc_config").([]interface{})) + } + + _, err := conn.UpdateFleetWithContext(ctx, input) + if err != nil { + return diag.FromErr(fmt.Errorf("error updating Appstream Fleet (%s): %w", d.Id(), err)) + } + + desiredState := d.Get("state") + if d.HasChange("state") { + if desiredState == "STOPPED" { + _, err := conn.StopFleetWithContext(ctx, &appstream.StopFleetInput{ + Name: aws.String(naming.Generate(d.Get("name").(string), d.Get("name_prefix").(string))), + }) + if err != nil { + return diag.FromErr(err) + } + for { + + resp, err := conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{ + Names: aws.StringSlice([]string{*input.Name}), + }) + if err != nil { + return diag.FromErr(fmt.Errorf("error describing Appstream Fleet (%s): %w", d.Id(), err)) + } + + currentState := resp.Fleets[0].State + if aws.StringValue(currentState) == desiredState { + break + } + if aws.StringValue(currentState) != desiredState { + time.Sleep(20 * time.Second) + continue + } + } + } else if desiredState == "RUNNING" { + _, err := conn.StartFleetWithContext(ctx, &appstream.StartFleetInput{ + Name: aws.String(naming.Generate(d.Get("name").(string), d.Get("name_prefix").(string))), + }) + if err != nil { + return diag.FromErr(err) + } + for { + + resp, err := conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{ + Names: aws.StringSlice([]string{*input.Name}), + }) + if err != nil { + return diag.FromErr(fmt.Errorf("error describing Appstream Fleet (%s): %w", d.Id(), err)) + } + + currentState := resp.Fleets[0].State + if aws.StringValue(currentState) == desiredState { + break + } + if aws.StringValue(currentState) != desiredState { + time.Sleep(20 * time.Second) + continue + } + + } + } + } + return resourceAwsAppstreamFleetRead(ctx, d, meta) + +} + +func resourceAwsAppstreamFleetDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).appstreamconn + + resp, err := conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{ + Names: aws.StringSlice([]string{*aws.String(d.Id())}), + }) + + if err != nil { + return diag.FromErr(fmt.Errorf("error reading Appstream Fleet (%s): %w", d.Id(), err)) + } + + currentState := aws.StringValue(resp.Fleets[0].State) + + if currentState == "RUNNING" { + desiredState := "STOPPED" + _, err = conn.StopFleet(&appstream.StopFleetInput{ + Name: aws.String(d.Id()), + }) + if err != nil { + return diag.FromErr(fmt.Errorf("error stopping Appstream Fleet (%s): %w", d.Id(), err)) + } + for { + + resp, err = conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{ + Names: aws.StringSlice([]string{*aws.String(d.Id())}), + }) + if err != nil { + return diag.FromErr(fmt.Errorf("error describing Appstream Fleet (%s): %w", d.Id(), err)) + } + + currentState := resp.Fleets[0].State + if aws.StringValue(currentState) == desiredState { + break + } + if aws.StringValue(currentState) != desiredState { + time.Sleep(20 * time.Second) + continue + } + + } + + } + + _, err = conn.DisassociateFleet(&appstream.DisassociateFleetInput{ + FleetName: aws.String(d.Id()), + StackName: aws.String(d.Get("stack_name").(string)), + }) + if err != nil { + return diag.FromErr(fmt.Errorf("error deleting Appstream Fleet (%s): %w", d.Id(), err)) + } + + _, err = conn.DeleteFleetWithContext(ctx, &appstream.DeleteFleetInput{ + Name: aws.String(d.Id()), + }) + if err != nil { + return diag.FromErr(fmt.Errorf("error deleting Appstream Fleet (%s): %w", d.Id(), err)) + } + return nil + +} + +func expandComputeCapacity(computeCapacity []interface{}) *appstream.ComputeCapacity { + if len(computeCapacity) == 0 { + return nil + } + + computeConf := &appstream.ComputeCapacity{} + + attr := computeCapacity[0].(map[string]interface{}) + if v, ok := attr["desired_instances"]; ok { + computeConf.DesiredInstances = aws.Int64(int64(v.(int))) + } + + return computeConf +} + +func flattenComputeCapacity(computeCapacity *appstream.ComputeCapacityStatus) []interface{} { + if computeCapacity == nil { + return nil + } + + compAttr := map[string]interface{}{} + compAttr["desired_instances"] = aws.Int64Value(computeCapacity.Desired) + + return []interface{}{compAttr} +} + +func expandDomainJoinInfo(domainInfo []interface{}) *appstream.DomainJoinInfo { + if len(domainInfo) == 0 { + return nil + } + + infoConfig := &appstream.DomainJoinInfo{} + + attr := domainInfo[0].(map[string]interface{}) + if v, ok := attr["directory_name"]; ok { + infoConfig.DirectoryName = aws.String(v.(string)) + } + if v, ok := attr["organizational_unit_distinguished_name"]; ok { + infoConfig.OrganizationalUnitDistinguishedName = aws.String(v.(string)) + } + + return infoConfig +} + +func flattenDomainInfo(domainInfo *appstream.DomainJoinInfo) []interface{} { + if domainInfo == nil { + return nil + } + + compAttr := map[string]interface{}{} + compAttr["directory_name"] = aws.StringValue(domainInfo.DirectoryName) + compAttr["organizational_unit_distinguished_name"] = aws.StringValue(domainInfo.OrganizationalUnitDistinguishedName) + + return []interface{}{compAttr} +} + +func expandVpcConfig(vpcConfig []interface{}) *appstream.VpcConfig { + if len(vpcConfig) == 0 { + return nil + } + + infoConfig := &appstream.VpcConfig{} + + attr := vpcConfig[0].(map[string]interface{}) + if v, ok := attr["security_group_ids"]; ok { + infoConfig.SecurityGroupIds = expandStringList(v.([]interface{})) + } + if v, ok := attr["subnet_ids"]; ok { + infoConfig.SubnetIds = expandStringList(v.([]interface{})) + } + + return infoConfig +} + +func flattenVpcConfig(vpcConfig *appstream.VpcConfig) []interface{} { + if vpcConfig == nil { + return nil + } + + compAttr := map[string]interface{}{} + compAttr["security_group_ids"] = aws.StringValueSlice(vpcConfig.SecurityGroupIds) + compAttr["subnet_ids"] = aws.StringValueSlice(vpcConfig.SubnetIds) + + return []interface{}{compAttr} +} diff --git a/aws/resource_aws_appstream_fleet_test.go b/aws/resource_aws_appstream_fleet_test.go new file mode 100644 index 00000000000..d95d17205ea --- /dev/null +++ b/aws/resource_aws_appstream_fleet_test.go @@ -0,0 +1,195 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/appstream" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func testAccAwsAppStreamFleet_basic(t *testing.T) { + var fleetOutput appstream.Fleet + resourceName := "aws_appstream_fleet.fleet" + fleetName := acctest.RandomWithPrefix("tf-acc-test") + instanceType := "stream.standard.small" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAwsAppStreamFleetDestroy, + ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), + Steps: []resource.TestStep{ + { + Config: testAccAwsAppStreamFleetConfigBasic(fleetName, instanceType), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + ), + }, + { + Config: testAccAwsAppStreamFleetConfigBasic(fleetName, instanceType), + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsAppStreamFleet_disappears(t *testing.T) { + var fleetOutput appstream.Fleet + resourceName := "aws_appstream_fleet.fleet" + fleetName := acctest.RandomWithPrefix("tf-acc-test") + instanceType := "stream.standard.small" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAwsAppStreamFleetDestroy, + ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), + Steps: []resource.TestStep{ + { + Config: testAccAwsAppStreamFleetConfigBasic(fleetName, instanceType), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + testAccCheckResourceDisappears(testAccProvider, resourceAwsAppstreamFleet(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccAwsAppStreamFleet_withTags(t *testing.T) { + var fleetOutput appstream.Fleet + resourceName := "aws_appstream_fleet.fleet" + fleetName := acctest.RandomWithPrefix("tf-acc-test") + description := "Description of a fleet" + fleetType := "ON_DEMAND" + instanceType := "stream.standard.small" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAwsAppStreamFleetDestroy, + ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), + Steps: []resource.TestStep{ + { + Config: testAccAwsAppStreamFleetConfigWithTags(fleetName, description, fleetType, instanceType), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + testAccCheckResourceAttrRfc3339(resourceName, "updated_at"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Key", "value"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.Key", "value"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckAwsAppStreamFleetExists(resourceName string, appStreamFleet *appstream.Fleet) 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).appstreamconn + resp, err := conn.DescribeFleets(&appstream.DescribeFleetsInput{Names: []*string{aws.String(rs.Primary.ID)}}) + + if err != nil { + return err + } + + if resp == nil && len(resp.Fleets) == 0 { + return fmt.Errorf("appstream fleet %q does not exist", rs.Primary.ID) + } + + *appStreamFleet = *resp.Fleets[0] + + return nil + } +} + +func testAccCheckAwsAppStreamFleetDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).appstreamconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_appstream_fleet" { + continue + } + + resp, err := conn.DescribeFleets(&appstream.DescribeFleetsInput{Names: []*string{aws.String(rs.Primary.ID)}}) + + if err != nil { + return err + } + + if resp != nil && len(resp.Fleets) > 0 { + return fmt.Errorf("appstream fleet %q still exists", rs.Primary.ID) + } + } + + return nil + +} + +func testAccAwsAppStreamFleetConfigBasic(fleetName, instaceType string) string { + return fmt.Sprintf(` +resource "aws_appstream_fleet" "test_fleet" { + name = %[1]q + compute_capacity { + desired_instances = 1 + } + instance_type = %[2]q +} +`, fleetName, instaceType) +} + +func testAccAwsAppStreamFleetConfigWithTags(fleetName, description, fleetType, instaceType string) string { + return fmt.Sprintf(` +data "aws_availability_zones" "available" { + state = "available" +} + +resource "aws_vpc" "example" { + cidr_block = "192.168.0.0/16" +} + +resource "aws_subnet" "example" { + availability_zone = data.aws_availability_zones.available.names[0] + cidr_block = "192.168.0.0/24" + vpc_id = aws_vpc.example.id +} + +resource "aws_appstream_fleet" "test_fleet" { + name = %[1]q + compute_capacity { + desired_instances = 1 + } + description = %[2]q + idle_disconnect_timeout_in_seconds = 70 + display_name = %[1]q + enable_default_internet_access = false + fleet_type = %[3]q + instance_type = %[4]q + max_user_duration_in_seconds = 600 + vpc_config { + subnet_ids = [aws_subnet.example.id] + } + tags = { + Key = "value" + } +} +`, fleetName, description, fleetType, instaceType) +} diff --git a/aws/resource_aws_appstream_test.go b/aws/resource_aws_appstream_test.go new file mode 100644 index 00000000000..833ffa6153a --- /dev/null +++ b/aws/resource_aws_appstream_test.go @@ -0,0 +1,27 @@ +package aws + +import ( + "testing" +) + +func TestAccAWSAppStreamResource_serial(t *testing.T) { + testCases := map[string]map[string]func(t *testing.T){ + "Fleet": { + "basic": testAccAwsAppStreamFleet_basic, + "tags": testAccAwsAppStreamFleet_withTags, + "disappears": testAccAwsAppStreamFleet_disappears, + }, + } + + for group, m := range testCases { + m := m + t.Run(group, func(t *testing.T) { + for name, tc := range m { + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } + }) + } +} diff --git a/website/docs/r/appstream_fleet.html.markdown b/website/docs/r/appstream_fleet.html.markdown new file mode 100644 index 00000000000..4f7e8e8879c --- /dev/null +++ b/website/docs/r/appstream_fleet.html.markdown @@ -0,0 +1,73 @@ +--- +subcategory: "AppStream" +layout: "aws" +page_title: "AWS: aws_appstream_fleet" +description: |- +Provides an AppStream fleet +--- + +# Resource: aws_appstream_fleet + +Provides an AppStream fleet. + +## Example Usage + +```hcl +resource "aws_appstream_fleet" "test_fleet" { + name = "test-fleet" + compute_capacity { + desired_instances = 1 + } + description = "test fleet" + idle_disconnect_timeout_in_seconds = 15 + display_name = "test-fleet" + enable_default_internet_access = false + fleet_type = "ON_DEMAND" + image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" + instance_type = "stream.standard.large" + max_user_duration_in_seconds = 600 + vpc_config { + subnet_ids = ["subnet-06e9b13400c225127"] + security_group_ids = ["sg-0397cdfe509785903", "sg-0bd2dddff01dee52d"] + } + tags = { + TagName = "tag-value" + } + state = "RUNNING" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) A unique name for the fleet. +* `name_prefix` - (Optional) Creates a unique name beginning with the specified prefix. Conflicts with `name`. +* `compute_capacity` - (Required) The desired capacity for the fleet. + * `desired_instances` - (Required) The desired number of streaming instances. +* `description` - (Optional) The description to display. +* `disconnect_timeout_in_seconds` - (Optional) The amount of time that a streaming session remains active after users disconnect. +* `display_name` - (Optional) Human-readable friendly name for the AppStream fleet. +* `domain_join_info` - (Optional) The name of the directory and organizational unit (OU) to use to join the fleet to a Microsoft Active Directory domain. + * `directory_name` - (Optional) The fully qualified name of the directory (for example, corp.example.com). + * `organizational_unit_distinguished_name` - (Optional) The distinguished name of the organizational unit for computer accounts. +* `enable_default_internet_access` - (Optional) Enables or disables default internet access for the fleet. +* `fleet_type` - (Optional) The fleet type. Valid values are: `ON_DEMAND`, `ALWAYS_ON` +* `iam_role_arn` - (Optional) The Amazon Resource Name (ARN) of the IAM role to apply to the fleet. +* `idle_disconnect_timeout_in_seconds` - (Optional) The amount of time that users can be idle (inactive) before they are disconnected from their streaming session and the `disconnect_timeout_in_seconds` time interval begins. +* `image_name` - (Optional) The name of the image used to create the fleet. +* `image_arn` - (Optional) The ARN of the public, private, or shared image to use. +* `instance_type` - (Required) The instance type to use when launching fleet instances. +* `stream_view` - (Optional) The AppStream 2.0 view that is displayed to your users when they stream from the fleet. When `APP` is specified, only the windows of applications opened by users display. When `DESKTOP` is specified, the standard desktop that is provided by the operating system displays. +* `max_user_duration_in_seconds` - (Optional) The maximum amount of time that a streaming session can remain active, in seconds. +* `stack_name` - (Optional) Name of AppStream stack, to be associated with this fleet. +* `state` - (Optional) The state of the fleet. Valid values are `RUNNING`, `STOPPED`. +* `vpc_config` - (Optional) The VPC configuration for the image builder. + * `security_group_ids` - The identifiers of the security groups for the fleet or image builder. + * `subnet_ids` - The identifiers of the subnets to which a network interface is attached from the fleet instance or image builder instance. +* `tags` - Map of tags to attach to AppStream instances. + +## Attributes Reference + +* `id` - The unique identifier (ID) of the appstream fleet. +* `arn` - The Amazon Resource Name (ARN) of the appstream fleet. From 8599a01658abed32d754b093f36fbdd85fbe33b5 Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Thu, 5 Aug 2021 18:12:34 -0600 Subject: [PATCH 02/28] refactor --- aws/resource_aws_appstream_fleet_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/aws/resource_aws_appstream_fleet_test.go b/aws/resource_aws_appstream_fleet_test.go index d95d17205ea..d60ef1c384a 100644 --- a/aws/resource_aws_appstream_fleet_test.go +++ b/aws/resource_aws_appstream_fleet_test.go @@ -81,7 +81,6 @@ func testAccAwsAppStreamFleet_withTags(t *testing.T) { Config: testAccAwsAppStreamFleetConfigWithTags(fleetName, description, fleetType, instanceType), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), - testAccCheckResourceAttrRfc3339(resourceName, "updated_at"), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.Key", "value"), resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), From 474365eec60cfe5c34c6c6d1836c01aaca7a19be Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Thu, 5 Aug 2021 18:43:54 -0600 Subject: [PATCH 03/28] deleted paaramter stack name --- aws/resource_aws_appstream_fleet.go | 26 +------------------- website/docs/r/appstream_fleet.html.markdown | 1 - 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/aws/resource_aws_appstream_fleet.go b/aws/resource_aws_appstream_fleet.go index 12821e0c919..8b455618aac 100644 --- a/aws/resource_aws_appstream_fleet.go +++ b/aws/resource_aws_appstream_fleet.go @@ -138,10 +138,6 @@ func resourceAwsAppstreamFleet() *schema.Resource { Computed: true, ValidateFunc: validation.StringInSlice(appstream.StreamView_Values(), false), }, - "stack_name": { - Type: schema.TypeString, - Optional: true, - }, "state": { Type: schema.TypeString, Optional: true, @@ -246,18 +242,6 @@ func resourceAwsAppstreamFleetCreate(ctx context.Context, d *schema.ResourceData return diag.FromErr(fmt.Errorf("error creating Appstream Fleet (%s): %w", d.Id(), err)) } - if v, ok := d.GetOk("stack_name"); ok { - associateInput := &appstream.AssociateFleetInput{} - associateInput.FleetName = input.Name - associateInput.StackName = aws.String(v.(string)) - resp, err := conn.AssociateFleet(associateInput) - if err != nil { - return diag.FromErr(fmt.Errorf("error associating Appstream Fleet (%s): %w", d.Id(), err)) - } - - log.Printf("[DEBUG] %s", resp) - } - if v, ok := d.GetOk("state"); ok { if v == "RUNNING" { desiredState := v @@ -365,7 +349,7 @@ func resourceAwsAppstreamFleetUpdate(ctx context.Context, d *schema.ResourceData conn := meta.(*AWSClient).appstreamconn input := &appstream.UpdateFleetInput{ - Name: aws.String(naming.Generate(d.Get("name").(string), d.Get("name_prefix").(string))), + Name: aws.String(d.Id()), } if d.HasChange("description") { @@ -527,14 +511,6 @@ func resourceAwsAppstreamFleetDelete(ctx context.Context, d *schema.ResourceData } - _, err = conn.DisassociateFleet(&appstream.DisassociateFleetInput{ - FleetName: aws.String(d.Id()), - StackName: aws.String(d.Get("stack_name").(string)), - }) - if err != nil { - return diag.FromErr(fmt.Errorf("error deleting Appstream Fleet (%s): %w", d.Id(), err)) - } - _, err = conn.DeleteFleetWithContext(ctx, &appstream.DeleteFleetInput{ Name: aws.String(d.Id()), }) diff --git a/website/docs/r/appstream_fleet.html.markdown b/website/docs/r/appstream_fleet.html.markdown index 4f7e8e8879c..9935cc935e1 100644 --- a/website/docs/r/appstream_fleet.html.markdown +++ b/website/docs/r/appstream_fleet.html.markdown @@ -60,7 +60,6 @@ The following arguments are supported: * `instance_type` - (Required) The instance type to use when launching fleet instances. * `stream_view` - (Optional) The AppStream 2.0 view that is displayed to your users when they stream from the fleet. When `APP` is specified, only the windows of applications opened by users display. When `DESKTOP` is specified, the standard desktop that is provided by the operating system displays. * `max_user_duration_in_seconds` - (Optional) The maximum amount of time that a streaming session can remain active, in seconds. -* `stack_name` - (Optional) Name of AppStream stack, to be associated with this fleet. * `state` - (Optional) The state of the fleet. Valid values are `RUNNING`, `STOPPED`. * `vpc_config` - (Optional) The VPC configuration for the image builder. * `security_group_ids` - The identifiers of the security groups for the fleet or image builder. From 6cedb6ed10c845d5c503c0828721af21f3e25765 Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Thu, 5 Aug 2021 18:59:19 -0600 Subject: [PATCH 04/28] refactor --- aws/resource_aws_appstream_fleet.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/aws/resource_aws_appstream_fleet.go b/aws/resource_aws_appstream_fleet.go index 8b455618aac..f9bdfca8a96 100644 --- a/aws/resource_aws_appstream_fleet.go +++ b/aws/resource_aws_appstream_fleet.go @@ -404,11 +404,20 @@ func resourceAwsAppstreamFleetUpdate(ctx context.Context, d *schema.ResourceData input.VpcConfig = expandVpcConfig(d.Get("vpc_config").([]interface{})) } - _, err := conn.UpdateFleetWithContext(ctx, input) + resp, err := conn.UpdateFleetWithContext(ctx, input) if err != nil { return diag.FromErr(fmt.Errorf("error updating Appstream Fleet (%s): %w", d.Id(), err)) } + if d.HasChange("tags") { + arn := aws.StringValue(resp.Fleet.Arn) + + o, n := d.GetChange("tags") + if err := keyvaluetags.AppstreamUpdateTags(conn, arn, o, n); err != nil { + return diag.FromErr(fmt.Errorf("error updating Appstream Fleet tags (%s): %w", d.Id(), err)) + } + } + desiredState := d.Get("state") if d.HasChange("state") { if desiredState == "STOPPED" { @@ -419,7 +428,6 @@ func resourceAwsAppstreamFleetUpdate(ctx context.Context, d *schema.ResourceData return diag.FromErr(err) } for { - resp, err := conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{ Names: aws.StringSlice([]string{*input.Name}), }) @@ -437,14 +445,13 @@ func resourceAwsAppstreamFleetUpdate(ctx context.Context, d *schema.ResourceData } } } else if desiredState == "RUNNING" { - _, err := conn.StartFleetWithContext(ctx, &appstream.StartFleetInput{ + _, err = conn.StartFleetWithContext(ctx, &appstream.StartFleetInput{ Name: aws.String(naming.Generate(d.Get("name").(string), d.Get("name_prefix").(string))), }) if err != nil { return diag.FromErr(err) } for { - resp, err := conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{ Names: aws.StringSlice([]string{*input.Name}), }) @@ -460,7 +467,6 @@ func resourceAwsAppstreamFleetUpdate(ctx context.Context, d *schema.ResourceData time.Sleep(20 * time.Second) continue } - } } } @@ -490,7 +496,6 @@ func resourceAwsAppstreamFleetDelete(ctx context.Context, d *schema.ResourceData return diag.FromErr(fmt.Errorf("error stopping Appstream Fleet (%s): %w", d.Id(), err)) } for { - resp, err = conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{ Names: aws.StringSlice([]string{*aws.String(d.Id())}), }) @@ -498,17 +503,15 @@ func resourceAwsAppstreamFleetDelete(ctx context.Context, d *schema.ResourceData return diag.FromErr(fmt.Errorf("error describing Appstream Fleet (%s): %w", d.Id(), err)) } - currentState := resp.Fleets[0].State - if aws.StringValue(currentState) == desiredState { + cState := resp.Fleets[0].State + if aws.StringValue(cState) == desiredState { break } - if aws.StringValue(currentState) != desiredState { + if aws.StringValue(cState) != desiredState { time.Sleep(20 * time.Second) continue } - } - } _, err = conn.DeleteFleetWithContext(ctx, &appstream.DeleteFleetInput{ From fdf2156faf2463f71eea400cd109497bd072d24f Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Fri, 6 Aug 2021 08:30:38 -0600 Subject: [PATCH 05/28] added validation for disappears --- aws/resource_aws_appstream_fleet.go | 35 ++++++++++++++++++-- aws/resource_aws_appstream_fleet_test.go | 5 +++ website/docs/r/appstream_fleet.html.markdown | 8 +++++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_appstream_fleet.go b/aws/resource_aws_appstream_fleet.go index f9bdfca8a96..d4de9fafa46 100644 --- a/aws/resource_aws_appstream_fleet.go +++ b/aws/resource_aws_appstream_fleet.go @@ -8,7 +8,9 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/appstream" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "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" @@ -237,7 +239,24 @@ func resourceAwsAppstreamFleetCreate(ctx context.Context, d *schema.ResourceData input.Tags = tags.IgnoreAws().AppstreamTags() } - resp, err := conn.CreateFleetWithContext(ctx, input) + var err error + var output *appstream.CreateFleetOutput + err = resource.RetryContext(ctx, 4*time.Minute, func() *resource.RetryError { + output, err = conn.CreateFleetWithContext(ctx, input) + if err != nil { + if tfawserr.ErrCodeEquals(err, appstream.ErrCodeResourceNotFoundException) { + return resource.RetryableError(err) + } + + return resource.NonRetryableError(err) + } + + return nil + }) + + if isResourceTimeoutError(err) { + output, err = conn.CreateFleetWithContext(ctx, input) + } if err != nil { return diag.FromErr(fmt.Errorf("error creating Appstream Fleet (%s): %w", d.Id(), err)) } @@ -246,7 +265,7 @@ func resourceAwsAppstreamFleetCreate(ctx context.Context, d *schema.ResourceData if v == "RUNNING" { desiredState := v _, err := conn.StartFleetWithContext(ctx, &appstream.StartFleetInput{ - Name: resp.Fleet.Name, + Name: output.Fleet.Name, }) if err != nil { @@ -273,7 +292,7 @@ func resourceAwsAppstreamFleetCreate(ctx context.Context, d *schema.ResourceData } } - d.SetId(aws.StringValue(resp.Fleet.Name)) + d.SetId(aws.StringValue(output.Fleet.Name)) return resourceAwsAppstreamFleetRead(ctx, d, meta) } @@ -285,10 +304,16 @@ func resourceAwsAppstreamFleetRead(ctx context.Context, d *schema.ResourceData, ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig resp, err := conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{Names: []*string{aws.String(d.Id())}}) + if tfawserr.ErrCodeEquals(err, appstream.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] Appstream Fleet (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } if err != nil { return diag.FromErr(fmt.Errorf("error reading Appstream Fleet (%s): %w", d.Id(), err)) } + for _, v := range resp.Fleets { d.Set("name", v.Name) d.Set("name_prefix", naming.NamePrefixFromName(aws.StringValue(v.Name))) @@ -517,7 +542,11 @@ func resourceAwsAppstreamFleetDelete(ctx context.Context, d *schema.ResourceData _, err = conn.DeleteFleetWithContext(ctx, &appstream.DeleteFleetInput{ Name: aws.String(d.Id()), }) + if err != nil { + if tfawserr.ErrCodeEquals(err, appstream.ErrCodeResourceNotFoundException) { + return nil + } return diag.FromErr(fmt.Errorf("error deleting Appstream Fleet (%s): %w", d.Id(), err)) } return nil diff --git a/aws/resource_aws_appstream_fleet_test.go b/aws/resource_aws_appstream_fleet_test.go index d60ef1c384a..9a155743857 100644 --- a/aws/resource_aws_appstream_fleet_test.go +++ b/aws/resource_aws_appstream_fleet_test.go @@ -6,6 +6,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/appstream" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -130,6 +131,10 @@ func testAccCheckAwsAppStreamFleetDestroy(s *terraform.State) error { resp, err := conn.DescribeFleets(&appstream.DescribeFleetsInput{Names: []*string{aws.String(rs.Primary.ID)}}) + if tfawserr.ErrCodeEquals(err, appstream.ErrCodeResourceNotFoundException) { + continue + } + if err != nil { return err } diff --git a/website/docs/r/appstream_fleet.html.markdown b/website/docs/r/appstream_fleet.html.markdown index 9935cc935e1..eab3c97d3e1 100644 --- a/website/docs/r/appstream_fleet.html.markdown +++ b/website/docs/r/appstream_fleet.html.markdown @@ -70,3 +70,11 @@ The following arguments are supported: * `id` - The unique identifier (ID) of the appstream fleet. * `arn` - The Amazon Resource Name (ARN) of the appstream fleet. + +## Import + +`aws_appstream_fleet` can be imported using the id, e.g. + +``` +$ terraform import aws_appstream_fleet.example abcd1 +``` From 007f737046b95267e5d5d1b0ebe51f7d63696af8 Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Fri, 6 Aug 2021 08:38:36 -0600 Subject: [PATCH 06/28] fixes a typo --- aws/provider.go | 2 +- aws/resource_aws_appstream_fleet.go | 22 +++++++++++----------- aws/resource_aws_appstream_fleet_test.go | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/aws/provider.go b/aws/provider.go index 62b74cf88a8..3cbb43aa570 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -537,7 +537,7 @@ func Provider() *schema.Provider { "aws_apprunner_custom_domain_association": resourceAwsAppRunnerCustomDomainAssociation(), "aws_apprunner_service": resourceAwsAppRunnerService(), "aws_appstream_stack": resourceAwsAppStreamStack(), - "aws_appstream_fleet": resourceAwsAppstreamFleet(), + "aws_appstream_fleet": resourceAwsAppStreamFleet(), "aws_appsync_api_key": resourceAwsAppsyncApiKey(), "aws_appsync_datasource": resourceAwsAppsyncDatasource(), "aws_appsync_function": resourceAwsAppsyncFunction(), diff --git a/aws/resource_aws_appstream_fleet.go b/aws/resource_aws_appstream_fleet.go index d4de9fafa46..6b944cee35d 100644 --- a/aws/resource_aws_appstream_fleet.go +++ b/aws/resource_aws_appstream_fleet.go @@ -17,12 +17,12 @@ import ( "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" ) -func resourceAwsAppstreamFleet() *schema.Resource { +func resourceAwsAppStreamFleet() *schema.Resource { return &schema.Resource{ - CreateWithoutTimeout: resourceAwsAppstreamFleetCreate, - ReadWithoutTimeout: resourceAwsAppstreamFleetRead, - UpdateWithoutTimeout: resourceAwsAppstreamFleetUpdate, - DeleteWithoutTimeout: resourceAwsAppstreamFleetDelete, + CreateWithoutTimeout: resourceAwsAppStreamFleetCreate, + ReadWithoutTimeout: resourceAwsAppStreamFleetRead, + UpdateWithoutTimeout: resourceAwsAppStreamFleetUpdate, + DeleteWithoutTimeout: resourceAwsAppStreamFleetDelete, Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -178,7 +178,7 @@ func resourceAwsAppstreamFleet() *schema.Resource { } } -func resourceAwsAppstreamFleetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceAwsAppStreamFleetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*AWSClient).appstreamconn input := &appstream.CreateFleetInput{ Name: aws.String(naming.Generate(d.Get("name").(string), d.Get("name_prefix").(string))), @@ -294,10 +294,10 @@ func resourceAwsAppstreamFleetCreate(ctx context.Context, d *schema.ResourceData d.SetId(aws.StringValue(output.Fleet.Name)) - return resourceAwsAppstreamFleetRead(ctx, d, meta) + return resourceAwsAppStreamFleetRead(ctx, d, meta) } -func resourceAwsAppstreamFleetRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceAwsAppStreamFleetRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*AWSClient).appstreamconn defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig @@ -370,7 +370,7 @@ func resourceAwsAppstreamFleetRead(ctx context.Context, d *schema.ResourceData, return nil } -func resourceAwsAppstreamFleetUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceAwsAppStreamFleetUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*AWSClient).appstreamconn input := &appstream.UpdateFleetInput{ @@ -495,11 +495,11 @@ func resourceAwsAppstreamFleetUpdate(ctx context.Context, d *schema.ResourceData } } } - return resourceAwsAppstreamFleetRead(ctx, d, meta) + return resourceAwsAppStreamFleetRead(ctx, d, meta) } -func resourceAwsAppstreamFleetDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceAwsAppStreamFleetDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*AWSClient).appstreamconn resp, err := conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{ diff --git a/aws/resource_aws_appstream_fleet_test.go b/aws/resource_aws_appstream_fleet_test.go index 9a155743857..015134f0a69 100644 --- a/aws/resource_aws_appstream_fleet_test.go +++ b/aws/resource_aws_appstream_fleet_test.go @@ -56,7 +56,7 @@ func testAccAwsAppStreamFleet_disappears(t *testing.T) { Config: testAccAwsAppStreamFleetConfigBasic(fleetName, instanceType), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), - testAccCheckResourceDisappears(testAccProvider, resourceAwsAppstreamFleet(), resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsAppStreamFleet(), resourceName), ), ExpectNonEmptyPlan: true, }, From 2f0103eea135112381fef83b4c5927fd110df37e Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Fri, 6 Aug 2021 08:47:04 -0600 Subject: [PATCH 07/28] fixes a linter error --- aws/resource_aws_appstream_fleet.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/aws/resource_aws_appstream_fleet.go b/aws/resource_aws_appstream_fleet.go index 6b944cee35d..90e94defc89 100644 --- a/aws/resource_aws_appstream_fleet.go +++ b/aws/resource_aws_appstream_fleet.go @@ -364,8 +364,6 @@ func resourceAwsAppStreamFleetRead(ctx context.Context, d *schema.ResourceData, if err = d.Set("tags_all", tags.Map()); err != nil { return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Stack (%s): %w", "tags_all", d.Id(), err)) } - - return nil } return nil } From 180aa4b3f9032fdc30a097b7bdc4bf8113cbeada Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Fri, 6 Aug 2021 10:07:16 -0600 Subject: [PATCH 08/28] terrafmt --- aws/resource_aws_appstream_fleet_test.go | 24 ++++++++++---------- website/docs/r/appstream_fleet.html.markdown | 22 +++++++++--------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/aws/resource_aws_appstream_fleet_test.go b/aws/resource_aws_appstream_fleet_test.go index 015134f0a69..1fcefaeefd4 100644 --- a/aws/resource_aws_appstream_fleet_test.go +++ b/aws/resource_aws_appstream_fleet_test.go @@ -151,11 +151,11 @@ func testAccCheckAwsAppStreamFleetDestroy(s *terraform.State) error { func testAccAwsAppStreamFleetConfigBasic(fleetName, instaceType string) string { return fmt.Sprintf(` resource "aws_appstream_fleet" "test_fleet" { - name = %[1]q + name = %[1]q compute_capacity { desired_instances = 1 } - instance_type = %[2]q + instance_type = %[2]q } `, fleetName, instaceType) } @@ -163,7 +163,7 @@ resource "aws_appstream_fleet" "test_fleet" { func testAccAwsAppStreamFleetConfigWithTags(fleetName, description, fleetType, instaceType string) string { return fmt.Sprintf(` data "aws_availability_zones" "available" { - state = "available" + state = "available" } resource "aws_vpc" "example" { @@ -177,19 +177,19 @@ resource "aws_subnet" "example" { } resource "aws_appstream_fleet" "test_fleet" { - name = %[1]q + name = %[1]q compute_capacity { desired_instances = 1 } - description = %[2]q - idle_disconnect_timeout_in_seconds = 70 - display_name = %[1]q - enable_default_internet_access = false - fleet_type = %[3]q - instance_type = %[4]q - max_user_duration_in_seconds = 600 + description = %[2]q + idle_disconnect_timeout_in_seconds = 70 + display_name = %[1]q + enable_default_internet_access = false + fleet_type = %[3]q + instance_type = %[4]q + max_user_duration_in_seconds = 600 vpc_config { - subnet_ids = [aws_subnet.example.id] + subnet_ids = [aws_subnet.example.id] } tags = { Key = "value" diff --git a/website/docs/r/appstream_fleet.html.markdown b/website/docs/r/appstream_fleet.html.markdown index eab3c97d3e1..c5f5ea6a1d5 100644 --- a/website/docs/r/appstream_fleet.html.markdown +++ b/website/docs/r/appstream_fleet.html.markdown @@ -14,21 +14,21 @@ Provides an AppStream fleet. ```hcl resource "aws_appstream_fleet" "test_fleet" { - name = "test-fleet" + name = "test-fleet" compute_capacity { desired_instances = 1 } - description = "test fleet" - idle_disconnect_timeout_in_seconds = 15 - display_name = "test-fleet" - enable_default_internet_access = false - fleet_type = "ON_DEMAND" - image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" - instance_type = "stream.standard.large" - max_user_duration_in_seconds = 600 + description = "test fleet" + idle_disconnect_timeout_in_seconds = 15 + display_name = "test-fleet" + enable_default_internet_access = false + fleet_type = "ON_DEMAND" + image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" + instance_type = "stream.standard.large" + max_user_duration_in_seconds = 600 vpc_config { - subnet_ids = ["subnet-06e9b13400c225127"] - security_group_ids = ["sg-0397cdfe509785903", "sg-0bd2dddff01dee52d"] + subnet_ids = ["subnet-06e9b13400c225127"] + security_group_ids = ["sg-0397cdfe509785903", "sg-0bd2dddff01dee52d"] } tags = { TagName = "tag-value" From 4c11e83bc2e24fd4e9f5d12f06748035fc4b3821 Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Fri, 6 Aug 2021 15:20:29 -0600 Subject: [PATCH 09/28] added status for starting/stopping fleet --- .../service/appstream/finder/finder.go | 25 +++- .../service/appstream/waiter/status.go | 18 +++ .../service/appstream/waiter/waiter.go | 41 ++++++ aws/resource_aws_appstream_fleet.go | 133 +++--------------- aws/resource_aws_appstream_fleet_test.go | 15 +- website/docs/r/appstream_fleet.html.markdown | 2 +- 6 files changed, 114 insertions(+), 120 deletions(-) diff --git a/aws/internal/service/appstream/finder/finder.go b/aws/internal/service/appstream/finder/finder.go index 8fce64010b9..8c7b7cb3cc6 100644 --- a/aws/internal/service/appstream/finder/finder.go +++ b/aws/internal/service/appstream/finder/finder.go @@ -21,7 +21,7 @@ func StackByName(ctx context.Context, conn *appstream.AppStream, name string) (* } if len(resp.Stacks) > 1 { - return nil, fmt.Errorf("[ERROR] got more than one stack with the name %s", name) + return nil, fmt.Errorf("got more than one stack with the name %s", name) } if len(resp.Stacks) == 1 { @@ -30,3 +30,26 @@ func StackByName(ctx context.Context, conn *appstream.AppStream, name string) (* return stack, nil } + +// FleetByName Retrieve a appstream fleet by name +func FleetByName(conn *appstream.AppStream, name string) (*appstream.Fleet, error) { + input := &appstream.DescribeFleetsInput{ + Names: []*string{aws.String(name)}, + } + + var fleet *appstream.Fleet + resp, err := conn.DescribeFleets(input) + if err != nil { + return nil, err + } + + if len(resp.Fleets) > 1 { + return nil, fmt.Errorf("got more than one fleet with the name %s", name) + } + + if len(resp.Fleets) == 1 { + fleet = resp.Fleets[0] + } + + return fleet, nil +} diff --git a/aws/internal/service/appstream/waiter/status.go b/aws/internal/service/appstream/waiter/status.go index 94428acc414..ea56020fd48 100644 --- a/aws/internal/service/appstream/waiter/status.go +++ b/aws/internal/service/appstream/waiter/status.go @@ -3,6 +3,7 @@ package waiter import ( "context" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/appstream" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/appstream/finder" @@ -23,3 +24,20 @@ func StackState(ctx context.Context, conn *appstream.AppStream, name string) res return stack, "AVAILABLE", nil } } + +//FleetState fetches the fleet and its state +func FleetState(conn *appstream.AppStream, name string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + fleet, err := finder.FleetByName(conn, name) + + if err != nil { + return nil, "Unknown", err + } + + if fleet == nil { + return fleet, "NotFound", nil + } + + return fleet, aws.StringValue(fleet.State), nil + } +} diff --git a/aws/internal/service/appstream/waiter/waiter.go b/aws/internal/service/appstream/waiter/waiter.go index 765170f881b..2cc5da4ccf5 100644 --- a/aws/internal/service/appstream/waiter/waiter.go +++ b/aws/internal/service/appstream/waiter/waiter.go @@ -11,6 +11,11 @@ import ( const ( // StackOperationTimeout Maximum amount of time to wait for Stack operation eventual consistency StackOperationTimeout = 4 * time.Minute + + // FleetStateTimeout Maximum amount of time to wait for the FleetState to be RUNNING or STOPPED + FleetStateTimeout = 30 * time.Minute + // FleetOperationTimeout Maximum amount of time to wait for Fleet operation eventual consistency + FleetOperationTimeout = 4 * time.Minute ) // StackStateDeleted waits for a deleted stack @@ -29,3 +34,39 @@ func StackStateDeleted(ctx context.Context, conn *appstream.AppStream, name stri return nil, err } + +// FleetStateRunning waits for a fleet running +func FleetStateRunning(ctx context.Context, conn *appstream.AppStream, name string) (*appstream.Fleet, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{appstream.FleetStateStarting}, + Target: []string{appstream.FleetStateRunning}, + Refresh: FleetState(conn, name), + Timeout: FleetStateTimeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*appstream.Fleet); ok { + return output, err + } + + return nil, err +} + +// FleetStateStopped waits for a fleet stopped +func FleetStateStopped(ctx context.Context, conn *appstream.AppStream, name string) (*appstream.Fleet, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{appstream.FleetStateStopping}, + Target: []string{appstream.FleetStateStopped}, + Refresh: FleetState(conn, name), + Timeout: FleetStateTimeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*appstream.Fleet); ok { + return output, err + } + + return nil, err +} diff --git a/aws/resource_aws_appstream_fleet.go b/aws/resource_aws_appstream_fleet.go index 90e94defc89..45696d5337e 100644 --- a/aws/resource_aws_appstream_fleet.go +++ b/aws/resource_aws_appstream_fleet.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/appstream/waiter" ) func resourceAwsAppStreamFleet() *schema.Resource { @@ -141,10 +142,8 @@ func resourceAwsAppStreamFleet() *schema.Resource { ValidateFunc: validation.StringInSlice(appstream.StreamView_Values(), false), }, "state": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validation.StringInSlice([]string{appstream.FleetStateRunning, appstream.FleetStateStopped}, false), + Type: schema.TypeString, + Computed: true, }, "vpc_config": { Type: schema.TypeList, @@ -261,35 +260,16 @@ func resourceAwsAppStreamFleetCreate(ctx context.Context, d *schema.ResourceData return diag.FromErr(fmt.Errorf("error creating Appstream Fleet (%s): %w", d.Id(), err)) } - if v, ok := d.GetOk("state"); ok { - if v == "RUNNING" { - desiredState := v - _, err := conn.StartFleetWithContext(ctx, &appstream.StartFleetInput{ - Name: output.Fleet.Name, - }) - - if err != nil { - return diag.FromErr(fmt.Errorf("error starting Appstream Fleet (%s): %w", d.Id(), err)) - } - for { - resp, err := conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{ - Names: aws.StringSlice([]string{*input.Name}), - }) - if err != nil { - return diag.FromErr(fmt.Errorf("error describing Appstream Fleet (%s): %w", d.Id(), err)) - } - - currentState := resp.Fleets[0].State - if aws.StringValue(currentState) == desiredState { - break - } - if aws.StringValue(currentState) != desiredState { - time.Sleep(20 * time.Second) - continue - } + // Start fleet workflow + _, err = conn.StartFleetWithContext(ctx, &appstream.StartFleetInput{ + Name: output.Fleet.Name, + }) + if err != nil { + return diag.FromErr(fmt.Errorf("error starting Appstream Fleet (%s): %w", d.Id(), err)) + } - } - } + if _, err = waiter.FleetStateRunning(ctx, conn, d.Id()); err != nil { + return diag.FromErr(fmt.Errorf("error waiting for Appstream Fleet (%s) to be running: %w", d.Id(), err)) } d.SetId(aws.StringValue(output.Fleet.Name)) @@ -441,58 +421,6 @@ func resourceAwsAppStreamFleetUpdate(ctx context.Context, d *schema.ResourceData } } - desiredState := d.Get("state") - if d.HasChange("state") { - if desiredState == "STOPPED" { - _, err := conn.StopFleetWithContext(ctx, &appstream.StopFleetInput{ - Name: aws.String(naming.Generate(d.Get("name").(string), d.Get("name_prefix").(string))), - }) - if err != nil { - return diag.FromErr(err) - } - for { - resp, err := conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{ - Names: aws.StringSlice([]string{*input.Name}), - }) - if err != nil { - return diag.FromErr(fmt.Errorf("error describing Appstream Fleet (%s): %w", d.Id(), err)) - } - - currentState := resp.Fleets[0].State - if aws.StringValue(currentState) == desiredState { - break - } - if aws.StringValue(currentState) != desiredState { - time.Sleep(20 * time.Second) - continue - } - } - } else if desiredState == "RUNNING" { - _, err = conn.StartFleetWithContext(ctx, &appstream.StartFleetInput{ - Name: aws.String(naming.Generate(d.Get("name").(string), d.Get("name_prefix").(string))), - }) - if err != nil { - return diag.FromErr(err) - } - for { - resp, err := conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{ - Names: aws.StringSlice([]string{*input.Name}), - }) - if err != nil { - return diag.FromErr(fmt.Errorf("error describing Appstream Fleet (%s): %w", d.Id(), err)) - } - - currentState := resp.Fleets[0].State - if aws.StringValue(currentState) == desiredState { - break - } - if aws.StringValue(currentState) != desiredState { - time.Sleep(20 * time.Second) - continue - } - } - } - } return resourceAwsAppStreamFleetRead(ctx, d, meta) } @@ -500,41 +428,16 @@ func resourceAwsAppStreamFleetUpdate(ctx context.Context, d *schema.ResourceData func resourceAwsAppStreamFleetDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*AWSClient).appstreamconn - resp, err := conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{ - Names: aws.StringSlice([]string{*aws.String(d.Id())}), + // Stop fleet workflow + _, err := conn.StopFleetWithContext(ctx, &appstream.StopFleetInput{ + Name: aws.String(d.Id()), }) - if err != nil { - return diag.FromErr(fmt.Errorf("error reading Appstream Fleet (%s): %w", d.Id(), err)) + return diag.FromErr(fmt.Errorf("error stopping Appstream Fleet (%s): %w", d.Id(), err)) } - currentState := aws.StringValue(resp.Fleets[0].State) - - if currentState == "RUNNING" { - desiredState := "STOPPED" - _, err = conn.StopFleet(&appstream.StopFleetInput{ - Name: aws.String(d.Id()), - }) - if err != nil { - return diag.FromErr(fmt.Errorf("error stopping Appstream Fleet (%s): %w", d.Id(), err)) - } - for { - resp, err = conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{ - Names: aws.StringSlice([]string{*aws.String(d.Id())}), - }) - if err != nil { - return diag.FromErr(fmt.Errorf("error describing Appstream Fleet (%s): %w", d.Id(), err)) - } - - cState := resp.Fleets[0].State - if aws.StringValue(cState) == desiredState { - break - } - if aws.StringValue(cState) != desiredState { - time.Sleep(20 * time.Second) - continue - } - } + if _, err = waiter.FleetStateStopped(ctx, conn, d.Id()); err != nil { + return diag.FromErr(fmt.Errorf("error waiting for Appstream Fleet (%s) to be stopped: %w", d.Id(), err)) } _, err = conn.DeleteFleetWithContext(ctx, &appstream.DeleteFleetInput{ diff --git a/aws/resource_aws_appstream_fleet_test.go b/aws/resource_aws_appstream_fleet_test.go index 1fcefaeefd4..4871d760c5e 100644 --- a/aws/resource_aws_appstream_fleet_test.go +++ b/aws/resource_aws_appstream_fleet_test.go @@ -19,7 +19,10 @@ func testAccAwsAppStreamFleet_basic(t *testing.T) { instanceType := "stream.standard.small" resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckHasIAMRole(t, "AmazonAppStreamServiceAccess") + }, ProviderFactories: testAccProviderFactories, CheckDestroy: testAccCheckAwsAppStreamFleetDestroy, ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), @@ -47,7 +50,10 @@ func testAccAwsAppStreamFleet_disappears(t *testing.T) { instanceType := "stream.standard.small" resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckHasIAMRole(t, "AmazonAppStreamServiceAccess") + }, ProviderFactories: testAccProviderFactories, CheckDestroy: testAccCheckAwsAppStreamFleetDestroy, ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), @@ -73,7 +79,10 @@ func testAccAwsAppStreamFleet_withTags(t *testing.T) { instanceType := "stream.standard.small" resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckHasIAMRole(t, "AmazonAppStreamServiceAccess") + }, ProviderFactories: testAccProviderFactories, CheckDestroy: testAccCheckAwsAppStreamFleetDestroy, ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), diff --git a/website/docs/r/appstream_fleet.html.markdown b/website/docs/r/appstream_fleet.html.markdown index c5f5ea6a1d5..d0ecd72d674 100644 --- a/website/docs/r/appstream_fleet.html.markdown +++ b/website/docs/r/appstream_fleet.html.markdown @@ -60,7 +60,6 @@ The following arguments are supported: * `instance_type` - (Required) The instance type to use when launching fleet instances. * `stream_view` - (Optional) The AppStream 2.0 view that is displayed to your users when they stream from the fleet. When `APP` is specified, only the windows of applications opened by users display. When `DESKTOP` is specified, the standard desktop that is provided by the operating system displays. * `max_user_duration_in_seconds` - (Optional) The maximum amount of time that a streaming session can remain active, in seconds. -* `state` - (Optional) The state of the fleet. Valid values are `RUNNING`, `STOPPED`. * `vpc_config` - (Optional) The VPC configuration for the image builder. * `security_group_ids` - The identifiers of the security groups for the fleet or image builder. * `subnet_ids` - The identifiers of the subnets to which a network interface is attached from the fleet instance or image builder instance. @@ -70,6 +69,7 @@ The following arguments are supported: * `id` - The unique identifier (ID) of the appstream fleet. * `arn` - The Amazon Resource Name (ARN) of the appstream fleet. +* `state` - The state of the fleet. ## Import From d2afb2a77b3f836cf507915a7d39c37e7612831f Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Fri, 6 Aug 2021 15:36:12 -0600 Subject: [PATCH 10/28] fixes example in doc fleet --- website/docs/r/appstream_fleet.html.markdown | 1 - 1 file changed, 1 deletion(-) diff --git a/website/docs/r/appstream_fleet.html.markdown b/website/docs/r/appstream_fleet.html.markdown index d0ecd72d674..5ea329c9623 100644 --- a/website/docs/r/appstream_fleet.html.markdown +++ b/website/docs/r/appstream_fleet.html.markdown @@ -33,7 +33,6 @@ resource "aws_appstream_fleet" "test_fleet" { tags = { TagName = "tag-value" } - state = "RUNNING" } ``` From d0860e03ea9280ce6b7b9aac132cd07b59638240 Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Tue, 10 Aug 2021 11:24:52 -0600 Subject: [PATCH 11/28] fixes for test --- aws/resource_aws_appstream_fleet.go | 46 +++- aws/resource_aws_appstream_fleet_test.go | 226 ++++++++++++++++--- aws/resource_aws_appstream_test.go | 9 +- website/docs/r/appstream_fleet.html.markdown | 7 +- 4 files changed, 255 insertions(+), 33 deletions(-) diff --git a/aws/resource_aws_appstream_fleet.go b/aws/resource_aws_appstream_fleet.go index 45696d5337e..a43c0440410 100644 --- a/aws/resource_aws_appstream_fleet.go +++ b/aws/resource_aws_appstream_fleet.go @@ -38,6 +38,18 @@ func resourceAwsAppStreamFleet() *schema.Resource { Type: schema.TypeInt, Required: true, }, + "available": { + Type: schema.TypeInt, + Computed: true, + }, + "in_use": { + Type: schema.TypeInt, + Computed: true, + }, + "running": { + Type: schema.TypeInt, + Computed: true, + }, }, }, }, @@ -198,6 +210,10 @@ func resourceAwsAppStreamFleetCreate(ctx context.Context, d *schema.ResourceData input.DisconnectTimeoutInSeconds = aws.Int64(int64(v.(int))) } + if v, ok := d.GetOk("idle_disconnect_timeout_in_seconds"); ok { + input.IdleDisconnectTimeoutInSeconds = aws.Int64(int64(v.(int))) + } + if v, ok := d.GetOk("display_name"); ok { input.DisplayName = aws.String(v.(string)) } @@ -268,7 +284,7 @@ func resourceAwsAppStreamFleetCreate(ctx context.Context, d *schema.ResourceData return diag.FromErr(fmt.Errorf("error starting Appstream Fleet (%s): %w", d.Id(), err)) } - if _, err = waiter.FleetStateRunning(ctx, conn, d.Id()); err != nil { + if _, err = waiter.FleetStateRunning(ctx, conn, aws.StringValue(output.Fleet.Name)); err != nil { return diag.FromErr(fmt.Errorf("error waiting for Appstream Fleet (%s) to be running: %w", d.Id(), err)) } @@ -349,12 +365,23 @@ func resourceAwsAppStreamFleetRead(ctx context.Context, d *schema.ResourceData, } func resourceAwsAppStreamFleetUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*AWSClient).appstreamconn input := &appstream.UpdateFleetInput{ Name: aws.String(d.Id()), } + // Stop fleet workflow + _, err := conn.StopFleetWithContext(ctx, &appstream.StopFleetInput{ + Name: aws.String(d.Id()), + }) + if err != nil { + return diag.FromErr(fmt.Errorf("error stopping Appstream Fleet (%s): %w", d.Id(), err)) + } + + if _, err = waiter.FleetStateStopped(ctx, conn, d.Id()); err != nil { + return diag.FromErr(fmt.Errorf("error waiting for Appstream Fleet (%s) to be stopped: %w", d.Id(), err)) + } + if d.HasChange("description") { input.Description = aws.String(d.Get("description").(string)) } @@ -421,6 +448,18 @@ func resourceAwsAppStreamFleetUpdate(ctx context.Context, d *schema.ResourceData } } + // Start fleet workflow + _, err = conn.StartFleetWithContext(ctx, &appstream.StartFleetInput{ + Name: aws.String(d.Id()), + }) + if err != nil { + return diag.FromErr(fmt.Errorf("error starting Appstream Fleet (%s): %w", d.Id(), err)) + } + + if _, err = waiter.FleetStateRunning(ctx, conn, d.Id()); err != nil { + return diag.FromErr(fmt.Errorf("error waiting for Appstream Fleet (%s) to be running: %w", d.Id(), err)) + } + return resourceAwsAppStreamFleetRead(ctx, d, meta) } @@ -476,6 +515,9 @@ func flattenComputeCapacity(computeCapacity *appstream.ComputeCapacityStatus) [] compAttr := map[string]interface{}{} compAttr["desired_instances"] = aws.Int64Value(computeCapacity.Desired) + compAttr["available"] = aws.Int64Value(computeCapacity.Available) + compAttr["in_use"] = aws.Int64Value(computeCapacity.InUse) + compAttr["running"] = aws.Int64Value(computeCapacity.Running) return []interface{}{compAttr} } diff --git a/aws/resource_aws_appstream_fleet_test.go b/aws/resource_aws_appstream_fleet_test.go index 4871d760c5e..bc016fa642a 100644 --- a/aws/resource_aws_appstream_fleet_test.go +++ b/aws/resource_aws_appstream_fleet_test.go @@ -7,15 +7,14 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/appstream" "github.com/hashicorp/aws-sdk-go-base/tfawserr" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" ) func testAccAwsAppStreamFleet_basic(t *testing.T) { var fleetOutput appstream.Fleet - resourceName := "aws_appstream_fleet.fleet" - fleetName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_appstream_fleet.test" instanceType := "stream.standard.small" resource.Test(t, resource.TestCase{ @@ -28,13 +27,81 @@ func testAccAwsAppStreamFleet_basic(t *testing.T) { ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), Steps: []resource.TestStep{ { - Config: testAccAwsAppStreamFleetConfigBasic(fleetName, instanceType), + Config: testAccAwsAppStreamFleetConfigNameGenerated(instanceType), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + naming.TestCheckResourceAttrNameGenerated(resourceName, "name"), + resource.TestCheckResourceAttr(resourceName, "name_prefix", "terraform-"), + resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), ), }, { - Config: testAccAwsAppStreamFleetConfigBasic(fleetName, instanceType), + Config: testAccAwsAppStreamFleetConfigNameGenerated(instanceType), + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsAppStreamFleet_Name_Generated(t *testing.T) { + var fleetOutput appstream.Fleet + resourceName := "aws_appstream_fleet.test" + instanceType := "stream.standard.small" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckHasIAMRole(t, "AmazonAppStreamServiceAccess") + }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAwsAppStreamFleetDestroy, + ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), + Steps: []resource.TestStep{ + { + Config: testAccAwsAppStreamFleetConfigNameGenerated(instanceType), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + naming.TestCheckResourceAttrNameGenerated(resourceName, "name"), + resource.TestCheckResourceAttr(resourceName, "name_prefix", "terraform-"), + ), + }, + { + Config: testAccAwsAppStreamFleetConfigNameGenerated(instanceType), + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsAppStreamFleet_NamePrefix(t *testing.T) { + var fleetOutput appstream.Fleet + resourceName := "aws_appstream_fleet.test" + instanceType := "stream.standard.small" + namePrefix := "tf-acc-test-prefix-" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckHasIAMRole(t, "AmazonAppStreamServiceAccess") + }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAwsAppStreamFleetDestroy, + ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), + Steps: []resource.TestStep{ + { + Config: testAccAwsAppStreamFleetConfigNamePrefix(instanceType, namePrefix), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + naming.TestCheckResourceAttrNameFromPrefix(resourceName, "name", namePrefix), + resource.TestCheckResourceAttr(resourceName, "name_prefix", namePrefix), + ), + }, + { + Config: testAccAwsAppStreamFleetConfigNameGenerated(instanceType), ResourceName: resourceName, ImportState: true, ImportStateVerify: true, @@ -45,8 +112,7 @@ func testAccAwsAppStreamFleet_basic(t *testing.T) { func testAccAwsAppStreamFleet_disappears(t *testing.T) { var fleetOutput appstream.Fleet - resourceName := "aws_appstream_fleet.fleet" - fleetName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_appstream_fleet.test" instanceType := "stream.standard.small" resource.Test(t, resource.TestCase{ @@ -59,7 +125,7 @@ func testAccAwsAppStreamFleet_disappears(t *testing.T) { ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), Steps: []resource.TestStep{ { - Config: testAccAwsAppStreamFleetConfigBasic(fleetName, instanceType), + Config: testAccAwsAppStreamFleetConfigNameGenerated(instanceType), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), testAccCheckResourceDisappears(testAccProvider, resourceAwsAppStreamFleet(), resourceName), @@ -70,13 +136,59 @@ func testAccAwsAppStreamFleet_disappears(t *testing.T) { }) } +func testAccAwsAppStreamFleet_Complete(t *testing.T) { + var fleetOutput appstream.Fleet + resourceName := "aws_appstream_fleet.test" + description := "Description of a test" + descriptionUpdated := "Updated Description of a test" + fleetType := "ON_DEMAND" + instanceType := "stream.standard.small" + instanceTypeUpdate := "stream.standard.medium" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckHasIAMRole(t, "AmazonAppStreamServiceAccess") + }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAwsAppStreamFleetDestroy, + ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), + Steps: []resource.TestStep{ + { + Config: testAccAwsAppStreamFleetConfigComplete(description, fleetType, instanceType), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), + resource.TestCheckResourceAttr(resourceName, "instance_type", instanceType), + resource.TestCheckResourceAttr(resourceName, "description", description), + ), + }, + { + Config: testAccAwsAppStreamFleetConfigComplete(descriptionUpdated, fleetType, instanceTypeUpdate), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), + resource.TestCheckResourceAttr(resourceName, "instance_type", instanceTypeUpdate), + resource.TestCheckResourceAttr(resourceName, "description", descriptionUpdated), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccAwsAppStreamFleet_withTags(t *testing.T) { var fleetOutput appstream.Fleet - resourceName := "aws_appstream_fleet.fleet" - fleetName := acctest.RandomWithPrefix("tf-acc-test") - description := "Description of a fleet" + resourceName := "aws_appstream_fleet.test" + description := "Description of a test" + descriptionUpdated := "Updated Description of a test" fleetType := "ON_DEMAND" instanceType := "stream.standard.small" + instanceTypeUpdate := "stream.standard.medium" resource.Test(t, resource.TestCase{ PreCheck: func() { @@ -88,9 +200,25 @@ func testAccAwsAppStreamFleet_withTags(t *testing.T) { ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), Steps: []resource.TestStep{ { - Config: testAccAwsAppStreamFleetConfigWithTags(fleetName, description, fleetType, instanceType), + Config: testAccAwsAppStreamFleetConfigWithTags(description, fleetType, instanceType), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), + resource.TestCheckResourceAttr(resourceName, "instance_type", instanceType), + resource.TestCheckResourceAttr(resourceName, "description", description), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Key", "value"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.Key", "value"), + ), + }, + { + Config: testAccAwsAppStreamFleetConfigWithTags(descriptionUpdated, fleetType, instanceTypeUpdate), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), + resource.TestCheckResourceAttr(resourceName, "instance_type", instanceTypeUpdate), + resource.TestCheckResourceAttr(resourceName, "description", descriptionUpdated), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.Key", "value"), resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), @@ -157,19 +285,66 @@ func testAccCheckAwsAppStreamFleetDestroy(s *terraform.State) error { } -func testAccAwsAppStreamFleetConfigBasic(fleetName, instaceType string) string { +func testAccAwsAppStreamFleetConfigNameGenerated(instaceType string) string { return fmt.Sprintf(` -resource "aws_appstream_fleet" "test_fleet" { - name = %[1]q +resource "aws_appstream_fleet" "test" { + image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" compute_capacity { desired_instances = 1 } - instance_type = %[2]q + instance_type = %[1]q +} +`, instaceType) +} + +func testAccAwsAppStreamFleetConfigNamePrefix(instaceType, namePrefix string) string { + return fmt.Sprintf(` +resource "aws_appstream_fleet" "test" { + image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" + compute_capacity { + desired_instances = 1 + } + instance_type = %[1]q + name_prefix = %[2]q +} +`, instaceType, namePrefix) +} + +func testAccAwsAppStreamFleetConfigComplete(description, fleetType, instaceType string) string { + return fmt.Sprintf(` +data "aws_availability_zones" "available" { + state = "available" +} + +resource "aws_vpc" "example" { + cidr_block = "192.168.0.0/16" +} + +resource "aws_subnet" "example" { + availability_zone = data.aws_availability_zones.available.names[0] + cidr_block = "192.168.0.0/24" + vpc_id = aws_vpc.example.id +} + +resource "aws_appstream_fleet" "test" { + image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" + compute_capacity { + desired_instances = 1 + } + description = %[1]q + idle_disconnect_timeout_in_seconds = 70 + enable_default_internet_access = false + fleet_type = %[2]q + instance_type = %[3]q + max_user_duration_in_seconds = 1000 + vpc_config { + subnet_ids = [aws_subnet.example.id] + } } -`, fleetName, instaceType) +`, description, fleetType, instaceType) } -func testAccAwsAppStreamFleetConfigWithTags(fleetName, description, fleetType, instaceType string) string { +func testAccAwsAppStreamFleetConfigWithTags(description, fleetType, instaceType string) string { return fmt.Sprintf(` data "aws_availability_zones" "available" { state = "available" @@ -185,18 +360,17 @@ resource "aws_subnet" "example" { vpc_id = aws_vpc.example.id } -resource "aws_appstream_fleet" "test_fleet" { - name = %[1]q +resource "aws_appstream_fleet" "test" { + image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" compute_capacity { desired_instances = 1 } - description = %[2]q + description = %[1]q idle_disconnect_timeout_in_seconds = 70 - display_name = %[1]q enable_default_internet_access = false - fleet_type = %[3]q - instance_type = %[4]q - max_user_duration_in_seconds = 600 + fleet_type = %[2]q + instance_type = %[3]q + max_user_duration_in_seconds = 1000 vpc_config { subnet_ids = [aws_subnet.example.id] } @@ -204,5 +378,5 @@ resource "aws_appstream_fleet" "test_fleet" { Key = "value" } } -`, fleetName, description, fleetType, instaceType) +`, description, fleetType, instaceType) } diff --git a/aws/resource_aws_appstream_test.go b/aws/resource_aws_appstream_test.go index 833ffa6153a..707412541f5 100644 --- a/aws/resource_aws_appstream_test.go +++ b/aws/resource_aws_appstream_test.go @@ -7,9 +7,12 @@ import ( func TestAccAWSAppStreamResource_serial(t *testing.T) { testCases := map[string]map[string]func(t *testing.T){ "Fleet": { - "basic": testAccAwsAppStreamFleet_basic, - "tags": testAccAwsAppStreamFleet_withTags, - "disappears": testAccAwsAppStreamFleet_disappears, + "basic": testAccAwsAppStreamFleet_basic, + "name_generated": testAccAwsAppStreamFleet_Name_Generated, + "name_prefix": testAccAwsAppStreamFleet_NamePrefix, + "complete": testAccAwsAppStreamFleet_Complete, + "tags": testAccAwsAppStreamFleet_withTags, + "disappears": testAccAwsAppStreamFleet_disappears, }, } diff --git a/website/docs/r/appstream_fleet.html.markdown b/website/docs/r/appstream_fleet.html.markdown index 5ea329c9623..46ee143d457 100644 --- a/website/docs/r/appstream_fleet.html.markdown +++ b/website/docs/r/appstream_fleet.html.markdown @@ -28,7 +28,6 @@ resource "aws_appstream_fleet" "test_fleet" { max_user_duration_in_seconds = 600 vpc_config { subnet_ids = ["subnet-06e9b13400c225127"] - security_group_ids = ["sg-0397cdfe509785903", "sg-0bd2dddff01dee52d"] } tags = { TagName = "tag-value" @@ -68,7 +67,11 @@ The following arguments are supported: * `id` - The unique identifier (ID) of the appstream fleet. * `arn` - The Amazon Resource Name (ARN) of the appstream fleet. -* `state` - The state of the fleet. +* `state` - The state of the fleet.* +* `compute_capacity` - The capacity for the fleet. + * `available` - The number of currently available instances that can be used to stream sessions. + * `in_use` - The number of instances in use for streaming. + * `running` - The total number of simultaneous streaming instances that are running. ## Import From 5423f86945dfd356b2d832a8103fce93b0dc4657 Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Tue, 10 Aug 2021 11:32:28 -0600 Subject: [PATCH 12/28] added created time --- aws/resource_aws_appstream_fleet.go | 5 +++++ aws/resource_aws_appstream_fleet_test.go | 5 +++++ website/docs/r/appstream_fleet.html.markdown | 3 ++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_appstream_fleet.go b/aws/resource_aws_appstream_fleet.go index a43c0440410..0752264a2ba 100644 --- a/aws/resource_aws_appstream_fleet.go +++ b/aws/resource_aws_appstream_fleet.go @@ -157,6 +157,10 @@ func resourceAwsAppStreamFleet() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, "vpc_config": { Type: schema.TypeList, MaxItems: 1, @@ -332,6 +336,7 @@ func resourceAwsAppStreamFleetRead(ctx context.Context, d *schema.ResourceData, d.Set("iam_role_arn", v.IamRoleArn) d.Set("stream_view", v.StreamView) d.Set("arn", v.Arn) + d.Set("created_time", aws.TimeValue(v.CreatedTime).Format(time.RFC3339)) d.Set("instance_type", v.InstanceType) d.Set("max_user_duration_in_seconds", v.MaxUserDurationInSeconds) diff --git a/aws/resource_aws_appstream_fleet_test.go b/aws/resource_aws_appstream_fleet_test.go index bc016fa642a..3f016ab3cb3 100644 --- a/aws/resource_aws_appstream_fleet_test.go +++ b/aws/resource_aws_appstream_fleet_test.go @@ -33,6 +33,7 @@ func testAccAwsAppStreamFleet_basic(t *testing.T) { naming.TestCheckResourceAttrNameGenerated(resourceName, "name"), resource.TestCheckResourceAttr(resourceName, "name_prefix", "terraform-"), resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), + testAccCheckResourceAttrRfc3339(resourceName, "created_time"), ), }, { @@ -161,6 +162,7 @@ func testAccAwsAppStreamFleet_Complete(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), resource.TestCheckResourceAttr(resourceName, "instance_type", instanceType), resource.TestCheckResourceAttr(resourceName, "description", description), + testAccCheckResourceAttrRfc3339(resourceName, "created_time"), ), }, { @@ -170,6 +172,7 @@ func testAccAwsAppStreamFleet_Complete(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), resource.TestCheckResourceAttr(resourceName, "instance_type", instanceTypeUpdate), resource.TestCheckResourceAttr(resourceName, "description", descriptionUpdated), + testAccCheckResourceAttrRfc3339(resourceName, "created_time"), ), }, { @@ -210,6 +213,7 @@ func testAccAwsAppStreamFleet_withTags(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "tags.Key", "value"), resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags_all.Key", "value"), + testAccCheckResourceAttrRfc3339(resourceName, "created_time"), ), }, { @@ -223,6 +227,7 @@ func testAccAwsAppStreamFleet_withTags(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "tags.Key", "value"), resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags_all.Key", "value"), + testAccCheckResourceAttrRfc3339(resourceName, "created_time"), ), }, { diff --git a/website/docs/r/appstream_fleet.html.markdown b/website/docs/r/appstream_fleet.html.markdown index 46ee143d457..3db477a8bdc 100644 --- a/website/docs/r/appstream_fleet.html.markdown +++ b/website/docs/r/appstream_fleet.html.markdown @@ -67,7 +67,8 @@ The following arguments are supported: * `id` - The unique identifier (ID) of the appstream fleet. * `arn` - The Amazon Resource Name (ARN) of the appstream fleet. -* `state` - The state of the fleet.* +* `state` - The state of the fleet. +* `created_time` - The date and time, in UTC and extended RFC 3339 format, when the fleet was created. * `compute_capacity` - The capacity for the fleet. * `available` - The number of currently available instances that can be used to stream sessions. * `in_use` - The number of instances in use for streaming. From fe0631ba2441b6eced171d693e8809381f71b0f5 Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Tue, 10 Aug 2021 20:09:29 -0600 Subject: [PATCH 13/28] fixes linter --- website/docs/r/appstream_fleet.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/appstream_fleet.html.markdown b/website/docs/r/appstream_fleet.html.markdown index 3db477a8bdc..d8eee88b7ea 100644 --- a/website/docs/r/appstream_fleet.html.markdown +++ b/website/docs/r/appstream_fleet.html.markdown @@ -27,7 +27,7 @@ resource "aws_appstream_fleet" "test_fleet" { instance_type = "stream.standard.large" max_user_duration_in_seconds = 600 vpc_config { - subnet_ids = ["subnet-06e9b13400c225127"] + subnet_ids = ["subnet-06e9b13400c225127"] } tags = { TagName = "tag-value" From 216fe74d5fb859575a38ecf33dac2105c0a7d9dd Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Thu, 12 Aug 2021 11:02:24 -0600 Subject: [PATCH 14/28] updated docs --- website/docs/r/appstream_fleet.html.markdown | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/website/docs/r/appstream_fleet.html.markdown b/website/docs/r/appstream_fleet.html.markdown index d8eee88b7ea..e88111892f0 100644 --- a/website/docs/r/appstream_fleet.html.markdown +++ b/website/docs/r/appstream_fleet.html.markdown @@ -65,11 +65,11 @@ The following arguments are supported: ## Attributes Reference -* `id` - The unique identifier (ID) of the appstream fleet. -* `arn` - The Amazon Resource Name (ARN) of the appstream fleet. -* `state` - The state of the fleet. +* `id` - A unique identifier (ID) of the appstream fleet. +* `arn` - Amazon Resource Name (ARN) of the appstream fleet. +* `state` - The state of the fleet. Can be `STARTING`, `RUNNING`, `STOPPING` or `STOPPED` * `created_time` - The date and time, in UTC and extended RFC 3339 format, when the fleet was created. -* `compute_capacity` - The capacity for the fleet. +* `compute_capacity` - Describes the capacity status for a fleet. * `available` - The number of currently available instances that can be used to stream sessions. * `in_use` - The number of instances in use for streaming. * `running` - The total number of simultaneous streaming instances that are running. @@ -79,5 +79,5 @@ The following arguments are supported: `aws_appstream_fleet` can be imported using the id, e.g. ``` -$ terraform import aws_appstream_fleet.example abcd1 +$ terraform import aws_appstream_fleet.example fleetNameExample ``` From f8a024a90ce1fe81b9188ac20c75e205593dcb57 Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Thu, 12 Aug 2021 13:44:11 -0600 Subject: [PATCH 15/28] fixes linter --- aws/resource_aws_appstream_fleet.go | 2 +- website/docs/r/appstream_fleet.html.markdown | 42 ++++++++++++++------ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/aws/resource_aws_appstream_fleet.go b/aws/resource_aws_appstream_fleet.go index 0752264a2ba..9c52ce6c2ec 100644 --- a/aws/resource_aws_appstream_fleet.go +++ b/aws/resource_aws_appstream_fleet.go @@ -304,7 +304,7 @@ func resourceAwsAppStreamFleetRead(ctx context.Context, d *schema.ResourceData, ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig resp, err := conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{Names: []*string{aws.String(d.Id())}}) - if tfawserr.ErrCodeEquals(err, appstream.ErrCodeResourceNotFoundException) { + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, appstream.ErrCodeResourceNotFoundException) { log.Printf("[WARN] Appstream Fleet (%s) not found, removing from state", d.Id()) d.SetId("") return nil diff --git a/website/docs/r/appstream_fleet.html.markdown b/website/docs/r/appstream_fleet.html.markdown index e88111892f0..e9c446d1039 100644 --- a/website/docs/r/appstream_fleet.html.markdown +++ b/website/docs/r/appstream_fleet.html.markdown @@ -3,7 +3,7 @@ subcategory: "AppStream" layout: "aws" page_title: "AWS: aws_appstream_fleet" description: |- -Provides an AppStream fleet + Provides an AppStream fleet --- # Resource: aws_appstream_fleet @@ -12,7 +12,7 @@ Provides an AppStream fleet. ## Example Usage -```hcl +```terraform resource "aws_appstream_fleet" "test_fleet" { name = "test-fleet" compute_capacity { @@ -41,14 +41,11 @@ The following arguments are supported: * `name` - (Required) A unique name for the fleet. * `name_prefix` - (Optional) Creates a unique name beginning with the specified prefix. Conflicts with `name`. -* `compute_capacity` - (Required) The desired capacity for the fleet. - * `desired_instances` - (Required) The desired number of streaming instances. +* `compute_capacity` - (Required) The desired capacity for the fleet. (documented below) * `description` - (Optional) The description to display. * `disconnect_timeout_in_seconds` - (Optional) The amount of time that a streaming session remains active after users disconnect. * `display_name` - (Optional) Human-readable friendly name for the AppStream fleet. -* `domain_join_info` - (Optional) The name of the directory and organizational unit (OU) to use to join the fleet to a Microsoft Active Directory domain. - * `directory_name` - (Optional) The fully qualified name of the directory (for example, corp.example.com). - * `organizational_unit_distinguished_name` - (Optional) The distinguished name of the organizational unit for computer accounts. +* `domain_join_info` - (Optional) The name of the directory and organizational unit (OU) to use to join the fleet to a Microsoft Active Directory domain. (documented below) * `enable_default_internet_access` - (Optional) Enables or disables default internet access for the fleet. * `fleet_type` - (Optional) The fleet type. Valid values are: `ON_DEMAND`, `ALWAYS_ON` * `iam_role_arn` - (Optional) The Amazon Resource Name (ARN) of the IAM role to apply to the fleet. @@ -58,21 +55,40 @@ The following arguments are supported: * `instance_type` - (Required) The instance type to use when launching fleet instances. * `stream_view` - (Optional) The AppStream 2.0 view that is displayed to your users when they stream from the fleet. When `APP` is specified, only the windows of applications opened by users display. When `DESKTOP` is specified, the standard desktop that is provided by the operating system displays. * `max_user_duration_in_seconds` - (Optional) The maximum amount of time that a streaming session can remain active, in seconds. -* `vpc_config` - (Optional) The VPC configuration for the image builder. - * `security_group_ids` - The identifiers of the security groups for the fleet or image builder. - * `subnet_ids` - The identifiers of the subnets to which a network interface is attached from the fleet instance or image builder instance. +* `vpc_config` - (Optional) The VPC configuration for the image builder. (documented below) * `tags` - Map of tags to attach to AppStream instances. +The `compute_capacity` object supports the following: + +* `desired_instances` - (Required) The desired number of streaming instances. + +The `domain_join_info` object supports the following: + +* `directory_name` - (Optional) The fully qualified name of the directory (for example, corp.example.com). +* `organizational_unit_distinguished_name` - (Optional) The distinguished name of the organizational unit for computer accounts. + +The `vpc_config` object supports the following: + +* `security_group_ids` - The identifiers of the security groups for the fleet or image builder. +* `subnet_ids` - The identifiers of the subnets to which a network interface is attached from the fleet instance or image builder instance. + + ## Attributes Reference +In addition to all arguments above, the following attributes are exported: + * `id` - A unique identifier (ID) of the appstream fleet. * `arn` - Amazon Resource Name (ARN) of the appstream fleet. * `state` - The state of the fleet. Can be `STARTING`, `RUNNING`, `STOPPING` or `STOPPED` * `created_time` - The date and time, in UTC and extended RFC 3339 format, when the fleet was created. * `compute_capacity` - Describes the capacity status for a fleet. - * `available` - The number of currently available instances that can be used to stream sessions. - * `in_use` - The number of instances in use for streaming. - * `running` - The total number of simultaneous streaming instances that are running. + +The `compute_capacity` object exports the following: + +* `available` - The number of currently available instances that can be used to stream sessions. +* `in_use` - The number of instances in use for streaming. +* `running` - The total number of simultaneous streaming instances that are running. + ## Import From b7a6deb224cb783b05a30ff63d1ed4d8d14b6967 Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Thu, 12 Aug 2021 13:44:49 -0600 Subject: [PATCH 16/28] added changelog --- .changelog/20543.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100755 .changelog/20543.txt diff --git a/.changelog/20543.txt b/.changelog/20543.txt new file mode 100755 index 00000000000..bb75acf566f --- /dev/null +++ b/.changelog/20543.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_appstream_fleet +``` \ No newline at end of file From 385ae9329f45dbdbecc88074ddf5bb7bca5d277b Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Thu, 12 Aug 2021 14:04:07 -0600 Subject: [PATCH 17/28] fixes linter --- aws/resource_aws_appstream_fleet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_appstream_fleet.go b/aws/resource_aws_appstream_fleet.go index 9c52ce6c2ec..e13abc0cc76 100644 --- a/aws/resource_aws_appstream_fleet.go +++ b/aws/resource_aws_appstream_fleet.go @@ -260,7 +260,7 @@ func resourceAwsAppStreamFleetCreate(ctx context.Context, d *schema.ResourceData var err error var output *appstream.CreateFleetOutput - err = resource.RetryContext(ctx, 4*time.Minute, func() *resource.RetryError { + err = resource.RetryContext(ctx, waiter.FleetOperationTimeout, func() *resource.RetryError { output, err = conn.CreateFleetWithContext(ctx, input) if err != nil { if tfawserr.ErrCodeEquals(err, appstream.ErrCodeResourceNotFoundException) { From 045a9c750a2ea598e75ee92894d369977dff2c3a Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Mon, 16 Aug 2021 19:06:16 -0600 Subject: [PATCH 18/28] made changes suggested by yakdriver --- aws/resource_aws_appstream_fleet.go | 170 ++++++++++--------- aws/resource_aws_appstream_fleet_test.go | 138 ++++++++++++--- aws/resource_aws_appstream_test.go | 30 ---- website/docs/r/appstream_fleet.html.markdown | 73 ++++---- 4 files changed, 238 insertions(+), 173 deletions(-) delete mode 100644 aws/resource_aws_appstream_test.go diff --git a/aws/resource_aws_appstream_fleet.go b/aws/resource_aws_appstream_fleet.go index e13abc0cc76..f2abddcfb50 100644 --- a/aws/resource_aws_appstream_fleet.go +++ b/aws/resource_aws_appstream_fleet.go @@ -28,20 +28,24 @@ func resourceAwsAppStreamFleet() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, "compute_capacity": { Type: schema.TypeList, MaxItems: 1, Required: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "desired_instances": { - Type: schema.TypeInt, - Required: true, - }, "available": { Type: schema.TypeInt, Computed: true, }, + "desired_instances": { + Type: schema.TypeInt, + Required: true, + }, "in_use": { Type: schema.TypeInt, Computed: true, @@ -53,6 +57,10 @@ func resourceAwsAppStreamFleet() *schema.Resource { }, }, }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, "description": { Type: schema.TypeString, Optional: true, @@ -157,10 +165,6 @@ func resourceAwsAppStreamFleet() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "created_time": { - Type: schema.TypeString, - Computed: true, - }, "vpc_config": { Type: schema.TypeList, MaxItems: 1, @@ -183,10 +187,6 @@ func resourceAwsAppStreamFleet() *schema.Resource { }, }, }, - "arn": { - Type: schema.TypeString, - Computed: true, - }, "tags": tagsSchema(), "tags_all": tagsSchemaComputed(), }, @@ -196,16 +196,14 @@ func resourceAwsAppStreamFleet() *schema.Resource { func resourceAwsAppStreamFleetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*AWSClient).appstreamconn input := &appstream.CreateFleetInput{ - Name: aws.String(naming.Generate(d.Get("name").(string), d.Get("name_prefix").(string))), + Name: aws.String(naming.Generate(d.Get("name").(string), d.Get("name_prefix").(string))), + InstanceType: aws.String(d.Get("instance_type").(string)), + ComputeCapacity: expandComputeCapacity(d.Get("compute_capacity").([]interface{})), } defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) - if v, ok := d.GetOk("compute_capacity"); ok { - input.ComputeCapacity = expandComputeCapacity(v.([]interface{})) - } - if v, ok := d.GetOk("description"); ok { input.Description = aws.String(v.(string)) } @@ -238,10 +236,6 @@ func resourceAwsAppStreamFleetCreate(ctx context.Context, d *schema.ResourceData input.ImageName = aws.String(v.(string)) } - if v, ok := d.GetOk("instance_type"); ok { - input.InstanceType = aws.String(v.(string)) - } - if v, ok := d.GetOk("iam_role_arn"); ok { input.IamRoleArn = aws.String(v.(string)) } @@ -315,37 +309,33 @@ func resourceAwsAppStreamFleetRead(ctx context.Context, d *schema.ResourceData, } for _, v := range resp.Fleets { - d.Set("name", v.Name) - d.Set("name_prefix", naming.NamePrefixFromName(aws.StringValue(v.Name))) - + d.Set("arn", v.Arn) if err = d.Set("compute_capacity", flattenComputeCapacity(v.ComputeCapacityStatus)); err != nil { return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Fleet (%s): %w", "compute_capacity", d.Id(), err)) } - if err = d.Set("domain_join_info", flattenDomainInfo(v.DomainJoinInfo)); err != nil { - return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Fleet (%s): %w", "domain_join_info", d.Id(), err)) - } - + d.Set("created_time", aws.TimeValue(v.CreatedTime).Format(time.RFC3339)) d.Set("description", v.Description) d.Set("display_name", v.DisplayName) d.Set("disconnect_timeout_in_seconds", v.DisconnectTimeoutInSeconds) + if err = d.Set("domain_join_info", flattenDomainInfo(v.DomainJoinInfo)); err != nil { + return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Fleet (%s): %w", "domain_join_info", d.Id(), err)) + } d.Set("idle_disconnect_timeout_in_seconds", v.IdleDisconnectTimeoutInSeconds) d.Set("enable_default_internet_access", v.EnableDefaultInternetAccess) d.Set("fleet_type", v.FleetType) + d.Set("iam_role_arn", v.IamRoleArn) d.Set("image_name", v.ImageName) d.Set("image_arn", v.ImageArn) - d.Set("iam_role_arn", v.IamRoleArn) - d.Set("stream_view", v.StreamView) - d.Set("arn", v.Arn) - d.Set("created_time", aws.TimeValue(v.CreatedTime).Format(time.RFC3339)) - d.Set("instance_type", v.InstanceType) d.Set("max_user_duration_in_seconds", v.MaxUserDurationInSeconds) + d.Set("name", v.Name) + d.Set("name_prefix", naming.NamePrefixFromName(aws.StringValue(v.Name))) + d.Set("state", v.State) + d.Set("stream_view", v.StreamView) if err = d.Set("vpc_config", flattenVpcConfig(v.VpcConfig)); err != nil { return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Fleet (%s): %w", "vpc_config", d.Id(), err)) } - d.Set("state", v.State) - tg, err := conn.ListTagsForResource(&appstream.ListTagsForResourceInput{ ResourceArn: v.Arn, }) @@ -374,27 +364,35 @@ func resourceAwsAppStreamFleetUpdate(ctx context.Context, d *schema.ResourceData input := &appstream.UpdateFleetInput{ Name: aws.String(d.Id()), } + shouldStop := false - // Stop fleet workflow - _, err := conn.StopFleetWithContext(ctx, &appstream.StopFleetInput{ - Name: aws.String(d.Id()), - }) - if err != nil { - return diag.FromErr(fmt.Errorf("error stopping Appstream Fleet (%s): %w", d.Id(), err)) + if d.HasChange("description") || d.HasChange("domain_join_info") || d.HasChange("enable_default_internet_access") || + d.HasChange("iam_role_arn") || d.HasChange("instance_type") || d.HasChange("max_user_duration_in_seconds") || + d.HasChange("stream_view") || d.HasChange("vpc_config") { + shouldStop = true } - if _, err = waiter.FleetStateStopped(ctx, conn, d.Id()); err != nil { - return diag.FromErr(fmt.Errorf("error waiting for Appstream Fleet (%s) to be stopped: %w", d.Id(), err)) - } - - if d.HasChange("description") { - input.Description = aws.String(d.Get("description").(string)) + // Stop fleet workflow if needed + if shouldStop { + _, err := conn.StopFleetWithContext(ctx, &appstream.StopFleetInput{ + Name: aws.String(d.Id()), + }) + if err != nil { + return diag.FromErr(fmt.Errorf("error stopping Appstream Fleet (%s): %w", d.Id(), err)) + } + if _, err = waiter.FleetStateStopped(ctx, conn, d.Id()); err != nil { + return diag.FromErr(fmt.Errorf("error waiting for Appstream Fleet (%s) to be stopped: %w", d.Id(), err)) + } } if d.HasChange("compute_capacity") { input.ComputeCapacity = expandComputeCapacity(d.Get("compute_capacity").([]interface{})) } + if d.HasChange("description") { + input.Description = aws.String(d.Get("description").(string)) + } + if d.HasChange("domain_join_info") { input.DomainJoinInfo = expandDomainJoinInfo(d.Get("domain_join_info").([]interface{})) } @@ -403,6 +401,10 @@ func resourceAwsAppStreamFleetUpdate(ctx context.Context, d *schema.ResourceData input.DisconnectTimeoutInSeconds = aws.Int64(int64(d.Get("disconnect_timeout_in_seconds").(int))) } + if d.HasChange("enable_default_internet_access") { + input.EnableDefaultInternetAccess = aws.Bool(d.Get("enable_default_internet_access").(bool)) + } + if d.HasChange("idle_disconnect_timeout_in_seconds") { input.IdleDisconnectTimeoutInSeconds = aws.Int64(int64(d.Get("idle_disconnect_timeout_in_seconds").(int))) } @@ -453,20 +455,21 @@ func resourceAwsAppStreamFleetUpdate(ctx context.Context, d *schema.ResourceData } } - // Start fleet workflow - _, err = conn.StartFleetWithContext(ctx, &appstream.StartFleetInput{ - Name: aws.String(d.Id()), - }) - if err != nil { - return diag.FromErr(fmt.Errorf("error starting Appstream Fleet (%s): %w", d.Id(), err)) - } + // Start fleet workflow if stopped + if shouldStop { + _, err = conn.StartFleetWithContext(ctx, &appstream.StartFleetInput{ + Name: aws.String(d.Id()), + }) + if err != nil { + return diag.FromErr(fmt.Errorf("error starting Appstream Fleet (%s): %w", d.Id(), err)) + } - if _, err = waiter.FleetStateRunning(ctx, conn, d.Id()); err != nil { - return diag.FromErr(fmt.Errorf("error waiting for Appstream Fleet (%s) to be running: %w", d.Id(), err)) + if _, err = waiter.FleetStateRunning(ctx, conn, d.Id()); err != nil { + return diag.FromErr(fmt.Errorf("error waiting for Appstream Fleet (%s) to be running: %w", d.Id(), err)) + } } return resourceAwsAppStreamFleetRead(ctx, d, meta) - } func resourceAwsAppStreamFleetDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -495,7 +498,6 @@ func resourceAwsAppStreamFleetDelete(ctx context.Context, d *schema.ResourceData return diag.FromErr(fmt.Errorf("error deleting Appstream Fleet (%s): %w", d.Id(), err)) } return nil - } func expandComputeCapacity(computeCapacity []interface{}) *appstream.ComputeCapacity { @@ -527,62 +529,62 @@ func flattenComputeCapacity(computeCapacity *appstream.ComputeCapacityStatus) [] return []interface{}{compAttr} } -func expandDomainJoinInfo(domainInfo []interface{}) *appstream.DomainJoinInfo { - if len(domainInfo) == 0 { +func expandDomainJoinInfo(tfList []interface{}) *appstream.DomainJoinInfo { + if len(tfList) == 0 { return nil } - infoConfig := &appstream.DomainJoinInfo{} + apiObject := &appstream.DomainJoinInfo{} - attr := domainInfo[0].(map[string]interface{}) + attr := tfList[0].(map[string]interface{}) if v, ok := attr["directory_name"]; ok { - infoConfig.DirectoryName = aws.String(v.(string)) + apiObject.DirectoryName = aws.String(v.(string)) } if v, ok := attr["organizational_unit_distinguished_name"]; ok { - infoConfig.OrganizationalUnitDistinguishedName = aws.String(v.(string)) + apiObject.OrganizationalUnitDistinguishedName = aws.String(v.(string)) } - return infoConfig + return apiObject } -func flattenDomainInfo(domainInfo *appstream.DomainJoinInfo) []interface{} { - if domainInfo == nil { +func flattenDomainInfo(apiObject *appstream.DomainJoinInfo) []interface{} { + if apiObject == nil { return nil } - compAttr := map[string]interface{}{} - compAttr["directory_name"] = aws.StringValue(domainInfo.DirectoryName) - compAttr["organizational_unit_distinguished_name"] = aws.StringValue(domainInfo.OrganizationalUnitDistinguishedName) + tfList := map[string]interface{}{} + tfList["directory_name"] = aws.StringValue(apiObject.DirectoryName) + tfList["organizational_unit_distinguished_name"] = aws.StringValue(apiObject.OrganizationalUnitDistinguishedName) - return []interface{}{compAttr} + return []interface{}{tfList} } -func expandVpcConfig(vpcConfig []interface{}) *appstream.VpcConfig { - if len(vpcConfig) == 0 { +func expandVpcConfig(tfList []interface{}) *appstream.VpcConfig { + if len(tfList) == 0 { return nil } - infoConfig := &appstream.VpcConfig{} + apiObject := &appstream.VpcConfig{} - attr := vpcConfig[0].(map[string]interface{}) + attr := tfList[0].(map[string]interface{}) if v, ok := attr["security_group_ids"]; ok { - infoConfig.SecurityGroupIds = expandStringList(v.([]interface{})) + apiObject.SecurityGroupIds = expandStringList(v.([]interface{})) } if v, ok := attr["subnet_ids"]; ok { - infoConfig.SubnetIds = expandStringList(v.([]interface{})) + apiObject.SubnetIds = expandStringList(v.([]interface{})) } - return infoConfig + return apiObject } -func flattenVpcConfig(vpcConfig *appstream.VpcConfig) []interface{} { - if vpcConfig == nil { +func flattenVpcConfig(apiObject *appstream.VpcConfig) []interface{} { + if apiObject == nil { return nil } - compAttr := map[string]interface{}{} - compAttr["security_group_ids"] = aws.StringValueSlice(vpcConfig.SecurityGroupIds) - compAttr["subnet_ids"] = aws.StringValueSlice(vpcConfig.SubnetIds) + tfList := map[string]interface{}{} + tfList["security_group_ids"] = aws.StringValueSlice(apiObject.SecurityGroupIds) + tfList["subnet_ids"] = aws.StringValueSlice(apiObject.SubnetIds) - return []interface{}{compAttr} + return []interface{}{tfList} } diff --git a/aws/resource_aws_appstream_fleet_test.go b/aws/resource_aws_appstream_fleet_test.go index 3f016ab3cb3..c2a5e48bf7d 100644 --- a/aws/resource_aws_appstream_fleet_test.go +++ b/aws/resource_aws_appstream_fleet_test.go @@ -12,12 +12,12 @@ import ( "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" ) -func testAccAwsAppStreamFleet_basic(t *testing.T) { +func TestAccAwsAppStreamFleet_basic(t *testing.T) { var fleetOutput appstream.Fleet resourceName := "aws_appstream_fleet.test" instanceType := "stream.standard.small" - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testAccPreCheckHasIAMRole(t, "AmazonAppStreamServiceAccess") @@ -46,12 +46,12 @@ func testAccAwsAppStreamFleet_basic(t *testing.T) { }) } -func testAccAwsAppStreamFleet_Name_Generated(t *testing.T) { +func TestAccAwsAppStreamFleet_nameGenerated(t *testing.T) { var fleetOutput appstream.Fleet resourceName := "aws_appstream_fleet.test" instanceType := "stream.standard.small" - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testAccPreCheckHasIAMRole(t, "AmazonAppStreamServiceAccess") @@ -78,13 +78,13 @@ func testAccAwsAppStreamFleet_Name_Generated(t *testing.T) { }) } -func testAccAwsAppStreamFleet_NamePrefix(t *testing.T) { +func TestAccAwsAppStreamFleet_namePrefix(t *testing.T) { var fleetOutput appstream.Fleet resourceName := "aws_appstream_fleet.test" instanceType := "stream.standard.small" namePrefix := "tf-acc-test-prefix-" - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testAccPreCheckHasIAMRole(t, "AmazonAppStreamServiceAccess") @@ -111,12 +111,12 @@ func testAccAwsAppStreamFleet_NamePrefix(t *testing.T) { }) } -func testAccAwsAppStreamFleet_disappears(t *testing.T) { +func TestAccAwsAppStreamFleet_disappears(t *testing.T) { var fleetOutput appstream.Fleet resourceName := "aws_appstream_fleet.test" instanceType := "stream.standard.small" - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testAccPreCheckHasIAMRole(t, "AmazonAppStreamServiceAccess") @@ -137,7 +137,7 @@ func testAccAwsAppStreamFleet_disappears(t *testing.T) { }) } -func testAccAwsAppStreamFleet_Complete(t *testing.T) { +func TestAccAwsAppStreamFleet_completeWithStop(t *testing.T) { var fleetOutput appstream.Fleet resourceName := "aws_appstream_fleet.test" description := "Description of a test" @@ -146,7 +146,7 @@ func testAccAwsAppStreamFleet_Complete(t *testing.T) { instanceType := "stream.standard.small" instanceTypeUpdate := "stream.standard.medium" - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testAccPreCheckHasIAMRole(t, "AmazonAppStreamServiceAccess") @@ -184,16 +184,66 @@ func testAccAwsAppStreamFleet_Complete(t *testing.T) { }) } -func testAccAwsAppStreamFleet_withTags(t *testing.T) { +func TestAccAwsAppStreamFleet_completeWithoutStop(t *testing.T) { var fleetOutput appstream.Fleet resourceName := "aws_appstream_fleet.test" description := "Description of a test" - descriptionUpdated := "Updated Description of a test" fleetType := "ON_DEMAND" instanceType := "stream.standard.small" - instanceTypeUpdate := "stream.standard.medium" + displayName := "display name of a test" + displayNameUpdated := "display name of a test updated" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckHasIAMRole(t, "AmazonAppStreamServiceAccess") + }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAwsAppStreamFleetDestroy, + ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), + Steps: []resource.TestStep{ + { + Config: testAccAwsAppStreamFleetConfigCompleteWithoutStopping(description, fleetType, instanceType, displayName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), + resource.TestCheckResourceAttr(resourceName, "instance_type", instanceType), + resource.TestCheckResourceAttr(resourceName, "description", description), + testAccCheckResourceAttrRfc3339(resourceName, "created_time"), + resource.TestCheckResourceAttr(resourceName, "display_name", displayName), + ), + }, + { + Config: testAccAwsAppStreamFleetConfigCompleteWithoutStopping(description, fleetType, instanceType, displayNameUpdated), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), + resource.TestCheckResourceAttr(resourceName, "instance_type", instanceType), + resource.TestCheckResourceAttr(resourceName, "description", description), + testAccCheckResourceAttrRfc3339(resourceName, "created_time"), + resource.TestCheckResourceAttr(resourceName, "description", description), + resource.TestCheckResourceAttr(resourceName, "display_name", displayNameUpdated), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAwsAppStreamFleet_withTags(t *testing.T) { + var fleetOutput appstream.Fleet + resourceName := "aws_appstream_fleet.test" + description := "Description of a test" + fleetType := "ON_DEMAND" + instanceType := "stream.standard.small" + displayName := "display name of a test" + displayNameUpdated := "display name of a test updated" - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testAccPreCheckHasIAMRole(t, "AmazonAppStreamServiceAccess") @@ -203,7 +253,7 @@ func testAccAwsAppStreamFleet_withTags(t *testing.T) { ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), Steps: []resource.TestStep{ { - Config: testAccAwsAppStreamFleetConfigWithTags(description, fleetType, instanceType), + Config: testAccAwsAppStreamFleetConfigWithTags(description, fleetType, instanceType, displayName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), @@ -217,12 +267,12 @@ func testAccAwsAppStreamFleet_withTags(t *testing.T) { ), }, { - Config: testAccAwsAppStreamFleetConfigWithTags(descriptionUpdated, fleetType, instanceTypeUpdate), + Config: testAccAwsAppStreamFleetConfigWithTags(description, fleetType, instanceType, displayNameUpdated), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), - resource.TestCheckResourceAttr(resourceName, "instance_type", instanceTypeUpdate), - resource.TestCheckResourceAttr(resourceName, "description", descriptionUpdated), + resource.TestCheckResourceAttr(resourceName, "instance_type", instanceType), + resource.TestCheckResourceAttr(resourceName, "description", description), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.Key", "value"), resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), @@ -290,7 +340,7 @@ func testAccCheckAwsAppStreamFleetDestroy(s *terraform.State) error { } -func testAccAwsAppStreamFleetConfigNameGenerated(instaceType string) string { +func testAccAwsAppStreamFleetConfigNameGenerated(instanceType string) string { return fmt.Sprintf(` resource "aws_appstream_fleet" "test" { image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" @@ -299,10 +349,10 @@ resource "aws_appstream_fleet" "test" { } instance_type = %[1]q } -`, instaceType) +`, instanceType) } -func testAccAwsAppStreamFleetConfigNamePrefix(instaceType, namePrefix string) string { +func testAccAwsAppStreamFleetConfigNamePrefix(instanceType, namePrefix string) string { return fmt.Sprintf(` resource "aws_appstream_fleet" "test" { image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" @@ -312,10 +362,44 @@ resource "aws_appstream_fleet" "test" { instance_type = %[1]q name_prefix = %[2]q } -`, instaceType, namePrefix) +`, instanceType, namePrefix) +} + +func testAccAwsAppStreamFleetConfigComplete(description, fleetType, instanceType string) string { + return fmt.Sprintf(` +data "aws_availability_zones" "available" { + state = "available" +} + +resource "aws_vpc" "example" { + cidr_block = "192.168.0.0/16" +} + +resource "aws_subnet" "example" { + availability_zone = data.aws_availability_zones.available.names[0] + cidr_block = "192.168.0.0/24" + vpc_id = aws_vpc.example.id +} + +resource "aws_appstream_fleet" "test" { + image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" + compute_capacity { + desired_instances = 1 + } + description = %[1]q + idle_disconnect_timeout_in_seconds = 70 + enable_default_internet_access = false + fleet_type = %[2]q + instance_type = %[3]q + max_user_duration_in_seconds = 1000 + vpc_config { + subnet_ids = [aws_subnet.example.id] + } +} +`, description, fleetType, instanceType) } -func testAccAwsAppStreamFleetConfigComplete(description, fleetType, instaceType string) string { +func testAccAwsAppStreamFleetConfigCompleteWithoutStopping(description, fleetType, instanceType, displayName string) string { return fmt.Sprintf(` data "aws_availability_zones" "available" { state = "available" @@ -337,6 +421,7 @@ resource "aws_appstream_fleet" "test" { desired_instances = 1 } description = %[1]q + display_name = %[4]q idle_disconnect_timeout_in_seconds = 70 enable_default_internet_access = false fleet_type = %[2]q @@ -346,10 +431,10 @@ resource "aws_appstream_fleet" "test" { subnet_ids = [aws_subnet.example.id] } } -`, description, fleetType, instaceType) +`, description, fleetType, instanceType, displayName) } -func testAccAwsAppStreamFleetConfigWithTags(description, fleetType, instaceType string) string { +func testAccAwsAppStreamFleetConfigWithTags(description, fleetType, instanceType, displayName string) string { return fmt.Sprintf(` data "aws_availability_zones" "available" { state = "available" @@ -371,6 +456,7 @@ resource "aws_appstream_fleet" "test" { desired_instances = 1 } description = %[1]q + display_name = %[4]q idle_disconnect_timeout_in_seconds = 70 enable_default_internet_access = false fleet_type = %[2]q @@ -383,5 +469,5 @@ resource "aws_appstream_fleet" "test" { Key = "value" } } -`, description, fleetType, instaceType) +`, description, fleetType, instanceType, displayName) } diff --git a/aws/resource_aws_appstream_test.go b/aws/resource_aws_appstream_test.go deleted file mode 100644 index 707412541f5..00000000000 --- a/aws/resource_aws_appstream_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package aws - -import ( - "testing" -) - -func TestAccAWSAppStreamResource_serial(t *testing.T) { - testCases := map[string]map[string]func(t *testing.T){ - "Fleet": { - "basic": testAccAwsAppStreamFleet_basic, - "name_generated": testAccAwsAppStreamFleet_Name_Generated, - "name_prefix": testAccAwsAppStreamFleet_NamePrefix, - "complete": testAccAwsAppStreamFleet_Complete, - "tags": testAccAwsAppStreamFleet_withTags, - "disappears": testAccAwsAppStreamFleet_disappears, - }, - } - - for group, m := range testCases { - m := m - t.Run(group, func(t *testing.T) { - for name, tc := range m { - tc := tc - t.Run(name, func(t *testing.T) { - tc(t) - }) - } - }) - } -} diff --git a/website/docs/r/appstream_fleet.html.markdown b/website/docs/r/appstream_fleet.html.markdown index e9c446d1039..d94dc1487bc 100644 --- a/website/docs/r/appstream_fleet.html.markdown +++ b/website/docs/r/appstream_fleet.html.markdown @@ -15,9 +15,11 @@ Provides an AppStream fleet. ```terraform resource "aws_appstream_fleet" "test_fleet" { name = "test-fleet" + compute_capacity { desired_instances = 1 } + description = "test fleet" idle_disconnect_timeout_in_seconds = 15 display_name = "test-fleet" @@ -26,9 +28,11 @@ resource "aws_appstream_fleet" "test_fleet" { image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" instance_type = "stream.standard.large" max_user_duration_in_seconds = 600 + vpc_config { subnet_ids = ["subnet-06e9b13400c225127"] } + tags = { TagName = "tag-value" } @@ -37,57 +41,60 @@ resource "aws_appstream_fleet" "test_fleet" { ## Argument Reference -The following arguments are supported: +The following arguments are required: -* `name` - (Required) A unique name for the fleet. -* `name_prefix` - (Optional) Creates a unique name beginning with the specified prefix. Conflicts with `name`. -* `compute_capacity` - (Required) The desired capacity for the fleet. (documented below) -* `description` - (Optional) The description to display. -* `disconnect_timeout_in_seconds` - (Optional) The amount of time that a streaming session remains active after users disconnect. +* `compute_capacity` - (Required) Configuration block for the desired capacity of the fleet. See below. +* `instance_type` - (Required) Instance type to use when launching fleet instances. + +The following arguments are optional: + +* `description` - (Optional) Description to display. +* `disconnect_timeout_in_seconds` - (Optional) Amount of time that a streaming session remains active after users disconnect. * `display_name` - (Optional) Human-readable friendly name for the AppStream fleet. -* `domain_join_info` - (Optional) The name of the directory and organizational unit (OU) to use to join the fleet to a Microsoft Active Directory domain. (documented below) +* `domain_join_info` - (Optional) Configuration block for the name of the directory and organizational unit (OU) to use to join the fleet to a Microsoft Active Directory domain. See below. * `enable_default_internet_access` - (Optional) Enables or disables default internet access for the fleet. -* `fleet_type` - (Optional) The fleet type. Valid values are: `ON_DEMAND`, `ALWAYS_ON` -* `iam_role_arn` - (Optional) The Amazon Resource Name (ARN) of the IAM role to apply to the fleet. -* `idle_disconnect_timeout_in_seconds` - (Optional) The amount of time that users can be idle (inactive) before they are disconnected from their streaming session and the `disconnect_timeout_in_seconds` time interval begins. -* `image_name` - (Optional) The name of the image used to create the fleet. -* `image_arn` - (Optional) The ARN of the public, private, or shared image to use. -* `instance_type` - (Required) The instance type to use when launching fleet instances. -* `stream_view` - (Optional) The AppStream 2.0 view that is displayed to your users when they stream from the fleet. When `APP` is specified, only the windows of applications opened by users display. When `DESKTOP` is specified, the standard desktop that is provided by the operating system displays. -* `max_user_duration_in_seconds` - (Optional) The maximum amount of time that a streaming session can remain active, in seconds. -* `vpc_config` - (Optional) The VPC configuration for the image builder. (documented below) -* `tags` - Map of tags to attach to AppStream instances. +* `fleet_type` - (Optional) Fleet type. Valid values are: `ON_DEMAND`, `ALWAYS_ON` +* `iam_role_arn` - (Optional) ARN of the IAM role to apply to the fleet. +* `idle_disconnect_timeout_in_seconds` - (Optional) Amount of time that users can be idle (inactive) before they are disconnected from their streaming session and the `disconnect_timeout_in_seconds` time interval begins. +* `image_name` - (Optional) Name of the image used to create the fleet. +* `image_arn` - (Optional) ARN of the public, private, or shared image to use. +* `name` - (Optional) Unique name for the fleet. +* `name_prefix` - (Optional) Creates a unique name beginning with the specified prefix. Conflicts with `name`. +* `stream_view` - (Optional) AppStream 2.0 view that is displayed to your users when they stream from the fleet. When `APP` is specified, only the windows of applications opened by users display. When `DESKTOP` is specified, the standard desktop that is provided by the operating system displays. +* `max_user_duration_in_seconds` - (Optional) Maximum amount of time that a streaming session can remain active, in seconds. +* `vpc_config` - (Optional) Configuration block for the VPC configuration for the image builder. See below. +* `tags` - (Optional) Map of tags to attach to AppStream instances. -The `compute_capacity` object supports the following: +### `compute_capacity` -* `desired_instances` - (Required) The desired number of streaming instances. +* `desired_instances` - (Required) Desired number of streaming instances. -The `domain_join_info` object supports the following: +### `domain_join_info` -* `directory_name` - (Optional) The fully qualified name of the directory (for example, corp.example.com). -* `organizational_unit_distinguished_name` - (Optional) The distinguished name of the organizational unit for computer accounts. +* `directory_name` - (Optional) Fully qualified name of the directory (for example, corp.example.com). +* `organizational_unit_distinguished_name` - (Optional) Distinguished name of the organizational unit for computer accounts. -The `vpc_config` object supports the following: +### `vpc_config` -* `security_group_ids` - The identifiers of the security groups for the fleet or image builder. -* `subnet_ids` - The identifiers of the subnets to which a network interface is attached from the fleet instance or image builder instance. +* `security_group_ids` - Identifiers of the security groups for the fleet or image builder. +* `subnet_ids` - Identifiers of the subnets to which a network interface is attached from the fleet instance or image builder instance. ## Attributes Reference In addition to all arguments above, the following attributes are exported: -* `id` - A unique identifier (ID) of the appstream fleet. -* `arn` - Amazon Resource Name (ARN) of the appstream fleet. -* `state` - The state of the fleet. Can be `STARTING`, `RUNNING`, `STOPPING` or `STOPPED` -* `created_time` - The date and time, in UTC and extended RFC 3339 format, when the fleet was created. +* `id` - Unique identifier (ID) of the appstream fleet. +* `arn` - ARN of the appstream fleet. +* `state` - State of the fleet. Can be `STARTING`, `RUNNING`, `STOPPING` or `STOPPED` +* `created_time` - Date and time, in UTC and extended RFC 3339 format, when the fleet was created. * `compute_capacity` - Describes the capacity status for a fleet. -The `compute_capacity` object exports the following: +### `compute_capacity` -* `available` - The number of currently available instances that can be used to stream sessions. -* `in_use` - The number of instances in use for streaming. -* `running` - The total number of simultaneous streaming instances that are running. +* `available` - Number of currently available instances that can be used to stream sessions. +* `in_use` - Number of instances in use for streaming. +* `running` - Total number of simultaneous streaming instances that are running. ## Import From ca4860f067f5966c7b969c6718694a012f7ddc24 Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Mon, 16 Aug 2021 19:31:56 -0600 Subject: [PATCH 19/28] fixes linter --- aws/resource_aws_appstream_fleet.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/aws/resource_aws_appstream_fleet.go b/aws/resource_aws_appstream_fleet.go index f2abddcfb50..4193ce47843 100644 --- a/aws/resource_aws_appstream_fleet.go +++ b/aws/resource_aws_appstream_fleet.go @@ -366,9 +366,7 @@ func resourceAwsAppStreamFleetUpdate(ctx context.Context, d *schema.ResourceData } shouldStop := false - if d.HasChange("description") || d.HasChange("domain_join_info") || d.HasChange("enable_default_internet_access") || - d.HasChange("iam_role_arn") || d.HasChange("instance_type") || d.HasChange("max_user_duration_in_seconds") || - d.HasChange("stream_view") || d.HasChange("vpc_config") { + if d.HasChanges("description", "domain_join_info", "enable_default_internet_access", "iam_role_arn", "instance_type", "max_user_duration_in_seconds", "stream_view", "vpc_config") { shouldStop = true } From 5eaaf03f585e262cd77ad32a06dc229d42dbd5fc Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Tue, 17 Aug 2021 15:19:47 -0600 Subject: [PATCH 20/28] refactor --- aws/resource_aws_appstream_fleet.go | 59 +++---- aws/resource_aws_appstream_fleet_test.go | 173 +++++-------------- website/docs/r/appstream_fleet.html.markdown | 3 +- 3 files changed, 71 insertions(+), 164 deletions(-) diff --git a/aws/resource_aws_appstream_fleet.go b/aws/resource_aws_appstream_fleet.go index 4193ce47843..d61a7fa2a64 100644 --- a/aws/resource_aws_appstream_fleet.go +++ b/aws/resource_aws_appstream_fleet.go @@ -14,7 +14,6 @@ import ( "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" - "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/appstream/waiter" ) @@ -142,18 +141,9 @@ func resourceAwsAppStreamFleet() *schema.Resource { ValidateFunc: validation.IntBetween(600, 360000), }, "name": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ConflictsWith: []string{"name_prefix"}, - }, - "name_prefix": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ConflictsWith: []string{"name"}, + Type: schema.TypeString, + Required: true, + ForceNew: true, }, "stream_view": { Type: schema.TypeString, @@ -196,7 +186,7 @@ func resourceAwsAppStreamFleet() *schema.Resource { func resourceAwsAppStreamFleetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*AWSClient).appstreamconn input := &appstream.CreateFleetInput{ - Name: aws.String(naming.Generate(d.Get("name").(string), d.Get("name_prefix").(string))), + Name: aws.String(d.Get("name").(string)), InstanceType: aws.String(d.Get("instance_type").(string)), ComputeCapacity: expandComputeCapacity(d.Get("compute_capacity").([]interface{})), } @@ -329,7 +319,6 @@ func resourceAwsAppStreamFleetRead(ctx context.Context, d *schema.ResourceData, d.Set("instance_type", v.InstanceType) d.Set("max_user_duration_in_seconds", v.MaxUserDurationInSeconds) d.Set("name", v.Name) - d.Set("name_prefix", naming.NamePrefixFromName(aws.StringValue(v.Name))) d.Set("state", v.State) d.Set("stream_view", v.StreamView) if err = d.Set("vpc_config", flattenVpcConfig(v.VpcConfig)); err != nil { @@ -498,33 +487,33 @@ func resourceAwsAppStreamFleetDelete(ctx context.Context, d *schema.ResourceData return nil } -func expandComputeCapacity(computeCapacity []interface{}) *appstream.ComputeCapacity { - if len(computeCapacity) == 0 { +func expandComputeCapacity(tfList []interface{}) *appstream.ComputeCapacity { + if len(tfList) == 0 { return nil } - computeConf := &appstream.ComputeCapacity{} + apiObject := &appstream.ComputeCapacity{} - attr := computeCapacity[0].(map[string]interface{}) + attr := tfList[0].(map[string]interface{}) if v, ok := attr["desired_instances"]; ok { - computeConf.DesiredInstances = aws.Int64(int64(v.(int))) + apiObject.DesiredInstances = aws.Int64(int64(v.(int))) } - return computeConf + return apiObject } -func flattenComputeCapacity(computeCapacity *appstream.ComputeCapacityStatus) []interface{} { - if computeCapacity == nil { +func flattenComputeCapacity(apiObject *appstream.ComputeCapacityStatus) []interface{} { + if apiObject == nil { return nil } - compAttr := map[string]interface{}{} - compAttr["desired_instances"] = aws.Int64Value(computeCapacity.Desired) - compAttr["available"] = aws.Int64Value(computeCapacity.Available) - compAttr["in_use"] = aws.Int64Value(computeCapacity.InUse) - compAttr["running"] = aws.Int64Value(computeCapacity.Running) + tfList := map[string]interface{}{} + tfList["desired_instances"] = aws.Int64Value(apiObject.Desired) + tfList["available"] = aws.Int64Value(apiObject.Available) + tfList["in_use"] = aws.Int64Value(apiObject.InUse) + tfList["running"] = aws.Int64Value(apiObject.Running) - return []interface{}{compAttr} + return []interface{}{tfList} } func expandDomainJoinInfo(tfList []interface{}) *appstream.DomainJoinInfo { @@ -534,11 +523,11 @@ func expandDomainJoinInfo(tfList []interface{}) *appstream.DomainJoinInfo { apiObject := &appstream.DomainJoinInfo{} - attr := tfList[0].(map[string]interface{}) - if v, ok := attr["directory_name"]; ok { + tfMap := tfList[0].(map[string]interface{}) + if v, ok := tfMap["directory_name"]; ok { apiObject.DirectoryName = aws.String(v.(string)) } - if v, ok := attr["organizational_unit_distinguished_name"]; ok { + if v, ok := tfMap["organizational_unit_distinguished_name"]; ok { apiObject.OrganizationalUnitDistinguishedName = aws.String(v.(string)) } @@ -564,11 +553,11 @@ func expandVpcConfig(tfList []interface{}) *appstream.VpcConfig { apiObject := &appstream.VpcConfig{} - attr := tfList[0].(map[string]interface{}) - if v, ok := attr["security_group_ids"]; ok { + tfMap := tfList[0].(map[string]interface{}) + if v, ok := tfMap["security_group_ids"]; ok { apiObject.SecurityGroupIds = expandStringList(v.([]interface{})) } - if v, ok := attr["subnet_ids"]; ok { + if v, ok := tfMap["subnet_ids"]; ok { apiObject.SubnetIds = expandStringList(v.([]interface{})) } diff --git a/aws/resource_aws_appstream_fleet_test.go b/aws/resource_aws_appstream_fleet_test.go index c2a5e48bf7d..de0277be971 100644 --- a/aws/resource_aws_appstream_fleet_test.go +++ b/aws/resource_aws_appstream_fleet_test.go @@ -7,15 +7,16 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/appstream" "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" - "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" ) func TestAccAwsAppStreamFleet_basic(t *testing.T) { var fleetOutput appstream.Fleet resourceName := "aws_appstream_fleet.test" instanceType := "stream.standard.small" + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { @@ -27,82 +28,16 @@ func TestAccAwsAppStreamFleet_basic(t *testing.T) { ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), Steps: []resource.TestStep{ { - Config: testAccAwsAppStreamFleetConfigNameGenerated(instanceType), + Config: testAccAwsAppStreamFleetConfig(rName, instanceType), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), - naming.TestCheckResourceAttrNameGenerated(resourceName, "name"), - resource.TestCheckResourceAttr(resourceName, "name_prefix", "terraform-"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "instance_type", instanceType), resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), testAccCheckResourceAttrRfc3339(resourceName, "created_time"), ), }, { - Config: testAccAwsAppStreamFleetConfigNameGenerated(instanceType), - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccAwsAppStreamFleet_nameGenerated(t *testing.T) { - var fleetOutput appstream.Fleet - resourceName := "aws_appstream_fleet.test" - instanceType := "stream.standard.small" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - testAccPreCheck(t) - testAccPreCheckHasIAMRole(t, "AmazonAppStreamServiceAccess") - }, - ProviderFactories: testAccProviderFactories, - CheckDestroy: testAccCheckAwsAppStreamFleetDestroy, - ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), - Steps: []resource.TestStep{ - { - Config: testAccAwsAppStreamFleetConfigNameGenerated(instanceType), - Check: resource.ComposeTestCheckFunc( - testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), - naming.TestCheckResourceAttrNameGenerated(resourceName, "name"), - resource.TestCheckResourceAttr(resourceName, "name_prefix", "terraform-"), - ), - }, - { - Config: testAccAwsAppStreamFleetConfigNameGenerated(instanceType), - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccAwsAppStreamFleet_namePrefix(t *testing.T) { - var fleetOutput appstream.Fleet - resourceName := "aws_appstream_fleet.test" - instanceType := "stream.standard.small" - namePrefix := "tf-acc-test-prefix-" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - testAccPreCheck(t) - testAccPreCheckHasIAMRole(t, "AmazonAppStreamServiceAccess") - }, - ProviderFactories: testAccProviderFactories, - CheckDestroy: testAccCheckAwsAppStreamFleetDestroy, - ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), - Steps: []resource.TestStep{ - { - Config: testAccAwsAppStreamFleetConfigNamePrefix(instanceType, namePrefix), - Check: resource.ComposeTestCheckFunc( - testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), - naming.TestCheckResourceAttrNameFromPrefix(resourceName, "name", namePrefix), - resource.TestCheckResourceAttr(resourceName, "name_prefix", namePrefix), - ), - }, - { - Config: testAccAwsAppStreamFleetConfigNameGenerated(instanceType), ResourceName: resourceName, ImportState: true, ImportStateVerify: true, @@ -115,6 +50,7 @@ func TestAccAwsAppStreamFleet_disappears(t *testing.T) { var fleetOutput appstream.Fleet resourceName := "aws_appstream_fleet.test" instanceType := "stream.standard.small" + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { @@ -126,7 +62,7 @@ func TestAccAwsAppStreamFleet_disappears(t *testing.T) { ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), Steps: []resource.TestStep{ { - Config: testAccAwsAppStreamFleetConfigNameGenerated(instanceType), + Config: testAccAwsAppStreamFleetConfig(rName, instanceType), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), testAccCheckResourceDisappears(testAccProvider, resourceAwsAppStreamFleet(), resourceName), @@ -140,6 +76,7 @@ func TestAccAwsAppStreamFleet_disappears(t *testing.T) { func TestAccAwsAppStreamFleet_completeWithStop(t *testing.T) { var fleetOutput appstream.Fleet resourceName := "aws_appstream_fleet.test" + rName := acctest.RandomWithPrefix("tf-acc-test") description := "Description of a test" descriptionUpdated := "Updated Description of a test" fleetType := "ON_DEMAND" @@ -156,9 +93,10 @@ func TestAccAwsAppStreamFleet_completeWithStop(t *testing.T) { ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), Steps: []resource.TestStep{ { - Config: testAccAwsAppStreamFleetConfigComplete(description, fleetType, instanceType), + Config: testAccAwsAppStreamFleetConfigComplete(rName, description, fleetType, instanceType), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), resource.TestCheckResourceAttr(resourceName, "instance_type", instanceType), resource.TestCheckResourceAttr(resourceName, "description", description), @@ -166,9 +104,10 @@ func TestAccAwsAppStreamFleet_completeWithStop(t *testing.T) { ), }, { - Config: testAccAwsAppStreamFleetConfigComplete(descriptionUpdated, fleetType, instanceTypeUpdate), + Config: testAccAwsAppStreamFleetConfigComplete(rName, descriptionUpdated, fleetType, instanceTypeUpdate), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), resource.TestCheckResourceAttr(resourceName, "instance_type", instanceTypeUpdate), resource.TestCheckResourceAttr(resourceName, "description", descriptionUpdated), @@ -187,6 +126,7 @@ func TestAccAwsAppStreamFleet_completeWithStop(t *testing.T) { func TestAccAwsAppStreamFleet_completeWithoutStop(t *testing.T) { var fleetOutput appstream.Fleet resourceName := "aws_appstream_fleet.test" + rName := acctest.RandomWithPrefix("tf-acc-test") description := "Description of a test" fleetType := "ON_DEMAND" instanceType := "stream.standard.small" @@ -203,9 +143,10 @@ func TestAccAwsAppStreamFleet_completeWithoutStop(t *testing.T) { ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), Steps: []resource.TestStep{ { - Config: testAccAwsAppStreamFleetConfigCompleteWithoutStopping(description, fleetType, instanceType, displayName), + Config: testAccAwsAppStreamFleetConfigCompleteWithoutStopping(rName, description, fleetType, instanceType, displayName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), resource.TestCheckResourceAttr(resourceName, "instance_type", instanceType), resource.TestCheckResourceAttr(resourceName, "description", description), @@ -214,9 +155,10 @@ func TestAccAwsAppStreamFleet_completeWithoutStop(t *testing.T) { ), }, { - Config: testAccAwsAppStreamFleetConfigCompleteWithoutStopping(description, fleetType, instanceType, displayNameUpdated), + Config: testAccAwsAppStreamFleetConfigCompleteWithoutStopping(rName, description, fleetType, instanceType, displayNameUpdated), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), resource.TestCheckResourceAttr(resourceName, "instance_type", instanceType), resource.TestCheckResourceAttr(resourceName, "description", description), @@ -237,6 +179,7 @@ func TestAccAwsAppStreamFleet_completeWithoutStop(t *testing.T) { func TestAccAwsAppStreamFleet_withTags(t *testing.T) { var fleetOutput appstream.Fleet resourceName := "aws_appstream_fleet.test" + rName := acctest.RandomWithPrefix("tf-acc-test") description := "Description of a test" fleetType := "ON_DEMAND" instanceType := "stream.standard.small" @@ -253,9 +196,10 @@ func TestAccAwsAppStreamFleet_withTags(t *testing.T) { ErrorCheck: testAccErrorCheck(t, appstream.EndpointsID), Steps: []resource.TestStep{ { - Config: testAccAwsAppStreamFleetConfigWithTags(description, fleetType, instanceType, displayName), + Config: testAccAwsAppStreamFleetConfigWithTags(rName, description, fleetType, instanceType, displayName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), resource.TestCheckResourceAttr(resourceName, "instance_type", instanceType), resource.TestCheckResourceAttr(resourceName, "description", description), @@ -267,9 +211,10 @@ func TestAccAwsAppStreamFleet_withTags(t *testing.T) { ), }, { - Config: testAccAwsAppStreamFleetConfigWithTags(description, fleetType, instanceType, displayNameUpdated), + Config: testAccAwsAppStreamFleetConfigWithTags(rName, description, fleetType, instanceType, displayNameUpdated), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppStreamFleetExists(resourceName, &fleetOutput), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning), resource.TestCheckResourceAttr(resourceName, "instance_type", instanceType), resource.TestCheckResourceAttr(resourceName, "description", description), @@ -340,32 +285,20 @@ func testAccCheckAwsAppStreamFleetDestroy(s *terraform.State) error { } -func testAccAwsAppStreamFleetConfigNameGenerated(instanceType string) string { - return fmt.Sprintf(` -resource "aws_appstream_fleet" "test" { - image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" - compute_capacity { - desired_instances = 1 - } - instance_type = %[1]q -} -`, instanceType) -} - -func testAccAwsAppStreamFleetConfigNamePrefix(instanceType, namePrefix string) string { +func testAccAwsAppStreamFleetConfig(name, instanceType string) string { return fmt.Sprintf(` resource "aws_appstream_fleet" "test" { + name = %[1]q image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" compute_capacity { desired_instances = 1 } - instance_type = %[1]q - name_prefix = %[2]q + instance_type = %[2]q } -`, instanceType, namePrefix) +`, name, instanceType) } -func testAccAwsAppStreamFleetConfigComplete(description, fleetType, instanceType string) string { +func testAccAwsAppStreamFleetConfigComplete(name, description, fleetType, instanceType string) string { return fmt.Sprintf(` data "aws_availability_zones" "available" { state = "available" @@ -382,24 +315,25 @@ resource "aws_subnet" "example" { } resource "aws_appstream_fleet" "test" { + name = %[1]q image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" compute_capacity { desired_instances = 1 } - description = %[1]q + description = %[2]q idle_disconnect_timeout_in_seconds = 70 enable_default_internet_access = false - fleet_type = %[2]q - instance_type = %[3]q + fleet_type = %[3]q + instance_type = %[4]q max_user_duration_in_seconds = 1000 vpc_config { subnet_ids = [aws_subnet.example.id] } } -`, description, fleetType, instanceType) +`, name, description, fleetType, instanceType) } -func testAccAwsAppStreamFleetConfigCompleteWithoutStopping(description, fleetType, instanceType, displayName string) string { +func testAccAwsAppStreamFleetConfigCompleteWithoutStopping(name, description, fleetType, instanceType, displayName string) string { return fmt.Sprintf(` data "aws_availability_zones" "available" { state = "available" @@ -416,58 +350,43 @@ resource "aws_subnet" "example" { } resource "aws_appstream_fleet" "test" { + name = %[1]q image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" compute_capacity { desired_instances = 1 } - description = %[1]q - display_name = %[4]q + description = %[2]q + display_name = %[5]q idle_disconnect_timeout_in_seconds = 70 enable_default_internet_access = false - fleet_type = %[2]q - instance_type = %[3]q + fleet_type = %[3]q + instance_type = %[4]q max_user_duration_in_seconds = 1000 vpc_config { subnet_ids = [aws_subnet.example.id] } } -`, description, fleetType, instanceType, displayName) +`, name, description, fleetType, instanceType, displayName) } -func testAccAwsAppStreamFleetConfigWithTags(description, fleetType, instanceType, displayName string) string { +func testAccAwsAppStreamFleetConfigWithTags(name, description, fleetType, instanceType, displayName string) string { return fmt.Sprintf(` -data "aws_availability_zones" "available" { - state = "available" -} - -resource "aws_vpc" "example" { - cidr_block = "192.168.0.0/16" -} - -resource "aws_subnet" "example" { - availability_zone = data.aws_availability_zones.available.names[0] - cidr_block = "192.168.0.0/24" - vpc_id = aws_vpc.example.id -} - resource "aws_appstream_fleet" "test" { + name = %[1]q image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" compute_capacity { desired_instances = 1 } - description = %[1]q - display_name = %[4]q + description = %[2]q + display_name = %[5]q idle_disconnect_timeout_in_seconds = 70 enable_default_internet_access = false - fleet_type = %[2]q - instance_type = %[3]q + fleet_type = %[3]q + instance_type = %[4]q max_user_duration_in_seconds = 1000 - vpc_config { - subnet_ids = [aws_subnet.example.id] - } tags = { Key = "value" } } -`, description, fleetType, instanceType, displayName) +`, name, description, fleetType, instanceType, displayName) } diff --git a/website/docs/r/appstream_fleet.html.markdown b/website/docs/r/appstream_fleet.html.markdown index d94dc1487bc..1adcba1e4a0 100644 --- a/website/docs/r/appstream_fleet.html.markdown +++ b/website/docs/r/appstream_fleet.html.markdown @@ -45,6 +45,7 @@ The following arguments are required: * `compute_capacity` - (Required) Configuration block for the desired capacity of the fleet. See below. * `instance_type` - (Required) Instance type to use when launching fleet instances. +* `name` - (Required) Unique name for the fleet. The following arguments are optional: @@ -58,8 +59,6 @@ The following arguments are optional: * `idle_disconnect_timeout_in_seconds` - (Optional) Amount of time that users can be idle (inactive) before they are disconnected from their streaming session and the `disconnect_timeout_in_seconds` time interval begins. * `image_name` - (Optional) Name of the image used to create the fleet. * `image_arn` - (Optional) ARN of the public, private, or shared image to use. -* `name` - (Optional) Unique name for the fleet. -* `name_prefix` - (Optional) Creates a unique name beginning with the specified prefix. Conflicts with `name`. * `stream_view` - (Optional) AppStream 2.0 view that is displayed to your users when they stream from the fleet. When `APP` is specified, only the windows of applications opened by users display. When `DESKTOP` is specified, the standard desktop that is provided by the operating system displays. * `max_user_duration_in_seconds` - (Optional) Maximum amount of time that a streaming session can remain active, in seconds. * `vpc_config` - (Optional) Configuration block for the VPC configuration for the image builder. See below. From 85898590dd238963253d3f9cc9d5f23bd022a4eb Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Tue, 24 Aug 2021 15:58:45 -0400 Subject: [PATCH 21/28] r/appstream_fleet: De-loop sets --- aws/resource_aws_appstream_fleet.go | 104 ++++++++++++++++------------ 1 file changed, 61 insertions(+), 43 deletions(-) diff --git a/aws/resource_aws_appstream_fleet.go b/aws/resource_aws_appstream_fleet.go index d61a7fa2a64..fc5094be9b6 100644 --- a/aws/resource_aws_appstream_fleet.go +++ b/aws/resource_aws_appstream_fleet.go @@ -288,6 +288,7 @@ func resourceAwsAppStreamFleetRead(ctx context.Context, d *schema.ResourceData, ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig resp, err := conn.DescribeFleetsWithContext(ctx, &appstream.DescribeFleetsInput{Names: []*string{aws.String(d.Id())}}) + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, appstream.ErrCodeResourceNotFoundException) { log.Printf("[WARN] Appstream Fleet (%s) not found, removing from state", d.Id()) d.SetId("") @@ -298,53 +299,70 @@ func resourceAwsAppStreamFleetRead(ctx context.Context, d *schema.ResourceData, return diag.FromErr(fmt.Errorf("error reading Appstream Fleet (%s): %w", d.Id(), err)) } - for _, v := range resp.Fleets { - d.Set("arn", v.Arn) - if err = d.Set("compute_capacity", flattenComputeCapacity(v.ComputeCapacityStatus)); err != nil { - return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Fleet (%s): %w", "compute_capacity", d.Id(), err)) - } - d.Set("created_time", aws.TimeValue(v.CreatedTime).Format(time.RFC3339)) - d.Set("description", v.Description) - d.Set("display_name", v.DisplayName) - d.Set("disconnect_timeout_in_seconds", v.DisconnectTimeoutInSeconds) - if err = d.Set("domain_join_info", flattenDomainInfo(v.DomainJoinInfo)); err != nil { - return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Fleet (%s): %w", "domain_join_info", d.Id(), err)) - } - d.Set("idle_disconnect_timeout_in_seconds", v.IdleDisconnectTimeoutInSeconds) - d.Set("enable_default_internet_access", v.EnableDefaultInternetAccess) - d.Set("fleet_type", v.FleetType) - d.Set("iam_role_arn", v.IamRoleArn) - d.Set("image_name", v.ImageName) - d.Set("image_arn", v.ImageArn) - d.Set("instance_type", v.InstanceType) - d.Set("max_user_duration_in_seconds", v.MaxUserDurationInSeconds) - d.Set("name", v.Name) - d.Set("state", v.State) - d.Set("stream_view", v.StreamView) - if err = d.Set("vpc_config", flattenVpcConfig(v.VpcConfig)); err != nil { - return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Fleet (%s): %w", "vpc_config", d.Id(), err)) - } + if len(resp.Fleets) == 0 { + return diag.FromErr(fmt.Errorf("error reading Appstream Fleet (%s): %s", d.Id(), "empty response")) + } - tg, err := conn.ListTagsForResource(&appstream.ListTagsForResourceInput{ - ResourceArn: v.Arn, - }) - if err != nil { - return diag.FromErr(fmt.Errorf("error listing stack tags for AppStream Stack (%s): %w", d.Id(), err)) - } - if tg.Tags == nil { - log.Printf("[DEBUG] Apsstream Stack tags (%s) not found", d.Id()) - return nil - } - tags := keyvaluetags.AppstreamKeyValueTags(tg.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig) + if len(resp.Fleets) > 1 { + return diag.FromErr(fmt.Errorf("error reading Appstream Fleet (%s): %s", d.Id(), "multiple fleets found")) + } - if err = d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { - return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Stack (%s): %w", "tags", d.Id(), err)) - } + fleet := resp.Fleets[0] - if err = d.Set("tags_all", tags.Map()); err != nil { - return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Stack (%s): %w", "tags_all", d.Id(), err)) - } + d.Set("arn", fleet.Arn) + + if err = d.Set("compute_capacity", flattenComputeCapacity(fleet.ComputeCapacityStatus)); err != nil { + return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Fleet (%s): %w", "compute_capacity", d.Id(), err)) + } + + d.Set("created_time", aws.TimeValue(fleet.CreatedTime).Format(time.RFC3339)) + d.Set("description", fleet.Description) + d.Set("display_name", fleet.DisplayName) + d.Set("disconnect_timeout_in_seconds", fleet.DisconnectTimeoutInSeconds) + + if err = d.Set("domain_join_info", flattenDomainInfo(fleet.DomainJoinInfo)); err != nil { + return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Fleet (%s): %w", "domain_join_info", d.Id(), err)) } + + d.Set("idle_disconnect_timeout_in_seconds", fleet.IdleDisconnectTimeoutInSeconds) + d.Set("enable_default_internet_access", fleet.EnableDefaultInternetAccess) + d.Set("fleet_type", fleet.FleetType) + d.Set("iam_role_arn", fleet.IamRoleArn) + d.Set("image_name", fleet.ImageName) + d.Set("image_arn", fleet.ImageArn) + d.Set("instance_type", fleet.InstanceType) + d.Set("max_user_duration_in_seconds", fleet.MaxUserDurationInSeconds) + d.Set("name", fleet.Name) + d.Set("state", fleet.State) + d.Set("stream_view", fleet.StreamView) + + if err = d.Set("vpc_config", flattenVpcConfig(fleet.VpcConfig)); err != nil { + return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Fleet (%s): %w", "vpc_config", d.Id(), err)) + } + + tg, err := conn.ListTagsForResource(&appstream.ListTagsForResourceInput{ + ResourceArn: fleet.Arn, + }) + + if err != nil { + return diag.FromErr(fmt.Errorf("error listing stack tags for AppStream Stack (%s): %w", d.Id(), err)) + } + + if tg.Tags == nil { + log.Printf("[DEBUG] AppStream Stack tags (%s) not found", d.Id()) + return nil + } + + tags := keyvaluetags.AppstreamKeyValueTags(tg.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig) + + if err = d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Stack (%s): %w", "tags", d.Id(), err)) + } + + if err = d.Set("tags_all", tags.Map()); err != nil { + return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream Stack (%s): %w", "tags_all", d.Id(), err)) + } + return nil } From aba0c3138937bb24244663c3024a6816f55e645d Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Tue, 24 Aug 2021 15:59:23 -0400 Subject: [PATCH 22/28] tests/r/appstream_fleet: Skip missing image fails on GovCloud --- aws/resource_aws_appstream_fleet_test.go | 33 +++++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_appstream_fleet_test.go b/aws/resource_aws_appstream_fleet_test.go index de0277be971..bb911a13353 100644 --- a/aws/resource_aws_appstream_fleet_test.go +++ b/aws/resource_aws_appstream_fleet_test.go @@ -12,10 +12,22 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) +func init() { + RegisterServiceErrorCheckFunc(appstream.EndpointsID, testAccErrorCheckSkipAppStream) +} + +// testAccErrorCheckSkipAppStream skips AppStream tests that have error messages indicating unsupported features +func testAccErrorCheckSkipAppStream(t *testing.T) resource.ErrorCheckFunc { + return testAccErrorCheckSkipMessagesContaining(t, + "ResourceNotFoundException: The image", + ) +} + func TestAccAwsAppStreamFleet_basic(t *testing.T) { var fleetOutput appstream.Fleet resourceName := "aws_appstream_fleet.test" instanceType := "stream.standard.small" + instanceType = "stream.standard.large" rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ @@ -50,6 +62,7 @@ func TestAccAwsAppStreamFleet_disappears(t *testing.T) { var fleetOutput appstream.Fleet resourceName := "aws_appstream_fleet.test" instanceType := "stream.standard.small" + instanceType = "stream.standard.large" rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ @@ -81,6 +94,7 @@ func TestAccAwsAppStreamFleet_completeWithStop(t *testing.T) { descriptionUpdated := "Updated Description of a test" fleetType := "ON_DEMAND" instanceType := "stream.standard.small" + instanceType = "stream.standard.large" instanceTypeUpdate := "stream.standard.medium" resource.ParallelTest(t, resource.TestCase{ @@ -130,6 +144,7 @@ func TestAccAwsAppStreamFleet_completeWithoutStop(t *testing.T) { description := "Description of a test" fleetType := "ON_DEMAND" instanceType := "stream.standard.small" + instanceType = "stream.standard.large" displayName := "display name of a test" displayNameUpdated := "display name of a test updated" @@ -282,18 +297,19 @@ func testAccCheckAwsAppStreamFleetDestroy(s *terraform.State) error { } return nil - } func testAccAwsAppStreamFleetConfig(name, instanceType string) string { + // "Amazon-AppStream2-Sample-Image-02-04-2019" is not available in GovCloud return fmt.Sprintf(` resource "aws_appstream_fleet" "test" { - name = %[1]q - image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" + name = %[1]q + image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" + instance_type = %[2]q + compute_capacity { desired_instances = 1 } - instance_type = %[2]q } `, name, instanceType) } @@ -317,15 +333,18 @@ resource "aws_subnet" "example" { resource "aws_appstream_fleet" "test" { name = %[1]q image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" + compute_capacity { desired_instances = 1 } + description = %[2]q idle_disconnect_timeout_in_seconds = 70 enable_default_internet_access = false fleet_type = %[3]q instance_type = %[4]q max_user_duration_in_seconds = 1000 + vpc_config { subnet_ids = [aws_subnet.example.id] } @@ -352,9 +371,11 @@ resource "aws_subnet" "example" { resource "aws_appstream_fleet" "test" { name = %[1]q image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" + compute_capacity { desired_instances = 1 } + description = %[2]q display_name = %[5]q idle_disconnect_timeout_in_seconds = 70 @@ -362,6 +383,7 @@ resource "aws_appstream_fleet" "test" { fleet_type = %[3]q instance_type = %[4]q max_user_duration_in_seconds = 1000 + vpc_config { subnet_ids = [aws_subnet.example.id] } @@ -374,9 +396,11 @@ func testAccAwsAppStreamFleetConfigWithTags(name, description, fleetType, instan resource "aws_appstream_fleet" "test" { name = %[1]q image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" + compute_capacity { desired_instances = 1 } + description = %[2]q display_name = %[5]q idle_disconnect_timeout_in_seconds = 70 @@ -384,6 +408,7 @@ resource "aws_appstream_fleet" "test" { fleet_type = %[3]q instance_type = %[4]q max_user_duration_in_seconds = 1000 + tags = { Key = "value" } From e3618b28d89a8498dd6ab49924a70ab4f39e37d1 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Tue, 24 Aug 2021 16:21:11 -0400 Subject: [PATCH 23/28] tests/r/appstream_fleet: Remove ineffectual --- aws/resource_aws_appstream_fleet_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/aws/resource_aws_appstream_fleet_test.go b/aws/resource_aws_appstream_fleet_test.go index bb911a13353..4c726c78320 100644 --- a/aws/resource_aws_appstream_fleet_test.go +++ b/aws/resource_aws_appstream_fleet_test.go @@ -27,7 +27,6 @@ func TestAccAwsAppStreamFleet_basic(t *testing.T) { var fleetOutput appstream.Fleet resourceName := "aws_appstream_fleet.test" instanceType := "stream.standard.small" - instanceType = "stream.standard.large" rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ @@ -62,7 +61,6 @@ func TestAccAwsAppStreamFleet_disappears(t *testing.T) { var fleetOutput appstream.Fleet resourceName := "aws_appstream_fleet.test" instanceType := "stream.standard.small" - instanceType = "stream.standard.large" rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ @@ -94,7 +92,6 @@ func TestAccAwsAppStreamFleet_completeWithStop(t *testing.T) { descriptionUpdated := "Updated Description of a test" fleetType := "ON_DEMAND" instanceType := "stream.standard.small" - instanceType = "stream.standard.large" instanceTypeUpdate := "stream.standard.medium" resource.ParallelTest(t, resource.TestCase{ @@ -144,7 +141,6 @@ func TestAccAwsAppStreamFleet_completeWithoutStop(t *testing.T) { description := "Description of a test" fleetType := "ON_DEMAND" instanceType := "stream.standard.small" - instanceType = "stream.standard.large" displayName := "display name of a test" displayNameUpdated := "display name of a test updated" From 84d1b94d307f580a8392b02fc2a74d31d9533672 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Tue, 31 Aug 2021 14:56:36 -0400 Subject: [PATCH 24/28] i/appstream: Add context --- aws/internal/service/appstream/finder/finder.go | 6 ++++-- aws/internal/service/appstream/waiter/status.go | 4 ++-- aws/internal/service/appstream/waiter/waiter.go | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/aws/internal/service/appstream/finder/finder.go b/aws/internal/service/appstream/finder/finder.go index 8c7b7cb3cc6..3dea74dac63 100644 --- a/aws/internal/service/appstream/finder/finder.go +++ b/aws/internal/service/appstream/finder/finder.go @@ -16,6 +16,7 @@ func StackByName(ctx context.Context, conn *appstream.AppStream, name string) (* var stack *appstream.Stack resp, err := conn.DescribeStacksWithContext(ctx, input) + if err != nil { return nil, err } @@ -32,13 +33,14 @@ func StackByName(ctx context.Context, conn *appstream.AppStream, name string) (* } // FleetByName Retrieve a appstream fleet by name -func FleetByName(conn *appstream.AppStream, name string) (*appstream.Fleet, error) { +func FleetByName(ctx context.Context, conn *appstream.AppStream, name string) (*appstream.Fleet, error) { input := &appstream.DescribeFleetsInput{ Names: []*string{aws.String(name)}, } var fleet *appstream.Fleet - resp, err := conn.DescribeFleets(input) + resp, err := conn.DescribeFleetsWithContext(ctx, input) + if err != nil { return nil, err } diff --git a/aws/internal/service/appstream/waiter/status.go b/aws/internal/service/appstream/waiter/status.go index ea56020fd48..d720c5aaa69 100644 --- a/aws/internal/service/appstream/waiter/status.go +++ b/aws/internal/service/appstream/waiter/status.go @@ -26,9 +26,9 @@ func StackState(ctx context.Context, conn *appstream.AppStream, name string) res } //FleetState fetches the fleet and its state -func FleetState(conn *appstream.AppStream, name string) resource.StateRefreshFunc { +func FleetState(ctx context.Context, conn *appstream.AppStream, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - fleet, err := finder.FleetByName(conn, name) + fleet, err := finder.FleetByName(ctx, conn, name) if err != nil { return nil, "Unknown", err diff --git a/aws/internal/service/appstream/waiter/waiter.go b/aws/internal/service/appstream/waiter/waiter.go index 2cc5da4ccf5..bb1943d9d65 100644 --- a/aws/internal/service/appstream/waiter/waiter.go +++ b/aws/internal/service/appstream/waiter/waiter.go @@ -13,7 +13,7 @@ const ( StackOperationTimeout = 4 * time.Minute // FleetStateTimeout Maximum amount of time to wait for the FleetState to be RUNNING or STOPPED - FleetStateTimeout = 30 * time.Minute + FleetStateTimeout = 60 * time.Minute // FleetOperationTimeout Maximum amount of time to wait for Fleet operation eventual consistency FleetOperationTimeout = 4 * time.Minute ) @@ -40,7 +40,7 @@ func FleetStateRunning(ctx context.Context, conn *appstream.AppStream, name stri stateConf := &resource.StateChangeConf{ Pending: []string{appstream.FleetStateStarting}, Target: []string{appstream.FleetStateRunning}, - Refresh: FleetState(conn, name), + Refresh: FleetState(ctx, conn, name), Timeout: FleetStateTimeout, } @@ -58,7 +58,7 @@ func FleetStateStopped(ctx context.Context, conn *appstream.AppStream, name stri stateConf := &resource.StateChangeConf{ Pending: []string{appstream.FleetStateStopping}, Target: []string{appstream.FleetStateStopped}, - Refresh: FleetState(conn, name), + Refresh: FleetState(ctx, conn, name), Timeout: FleetStateTimeout, } From b769f29726f355fed9d496aa65d5cea92a5ef63e Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Wed, 22 Sep 2021 15:06:41 -0400 Subject: [PATCH 25/28] tests/appstream/fleet: Update subnet configs, timeouts --- .../service/appstream/waiter/waiter.go | 4 +- aws/resource_aws_appstream_fleet_test.go | 71 +++++++++++-------- 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/aws/internal/service/appstream/waiter/waiter.go b/aws/internal/service/appstream/waiter/waiter.go index bb1943d9d65..c6f0db45833 100644 --- a/aws/internal/service/appstream/waiter/waiter.go +++ b/aws/internal/service/appstream/waiter/waiter.go @@ -13,9 +13,9 @@ const ( StackOperationTimeout = 4 * time.Minute // FleetStateTimeout Maximum amount of time to wait for the FleetState to be RUNNING or STOPPED - FleetStateTimeout = 60 * time.Minute + FleetStateTimeout = 180 * time.Minute // FleetOperationTimeout Maximum amount of time to wait for Fleet operation eventual consistency - FleetOperationTimeout = 4 * time.Minute + FleetOperationTimeout = 15 * time.Minute ) // StackStateDeleted waits for a deleted stack diff --git a/aws/resource_aws_appstream_fleet_test.go b/aws/resource_aws_appstream_fleet_test.go index 4c726c78320..08b4ad32d62 100644 --- a/aws/resource_aws_appstream_fleet_test.go +++ b/aws/resource_aws_appstream_fleet_test.go @@ -311,19 +311,18 @@ resource "aws_appstream_fleet" "test" { } func testAccAwsAppStreamFleetConfigComplete(name, description, fleetType, instanceType string) string { - return fmt.Sprintf(` -data "aws_availability_zones" "available" { - state = "available" -} - -resource "aws_vpc" "example" { - cidr_block = "192.168.0.0/16" + return composeConfig( + testAccAvailableAZsNoOptInConfig(), + fmt.Sprintf(` +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" } -resource "aws_subnet" "example" { - availability_zone = data.aws_availability_zones.available.names[0] - cidr_block = "192.168.0.0/24" - vpc_id = aws_vpc.example.id +resource "aws_subnet" "test" { + count = 2 + availability_zone = data.aws_availability_zones.available.names[count.index] + cidr_block = "10.0.${count.index}.0/24" + vpc_id = aws_vpc.test.id } resource "aws_appstream_fleet" "test" { @@ -342,26 +341,25 @@ resource "aws_appstream_fleet" "test" { max_user_duration_in_seconds = 1000 vpc_config { - subnet_ids = [aws_subnet.example.id] + subnet_ids = aws_subnet.test.*.id } } -`, name, description, fleetType, instanceType) +`, name, description, fleetType, instanceType)) } func testAccAwsAppStreamFleetConfigCompleteWithoutStopping(name, description, fleetType, instanceType, displayName string) string { - return fmt.Sprintf(` -data "aws_availability_zones" "available" { - state = "available" + return composeConfig( + testAccAvailableAZsNoOptInConfig(), + fmt.Sprintf(` +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" } -resource "aws_vpc" "example" { - cidr_block = "192.168.0.0/16" -} - -resource "aws_subnet" "example" { - availability_zone = data.aws_availability_zones.available.names[0] - cidr_block = "192.168.0.0/24" - vpc_id = aws_vpc.example.id +resource "aws_subnet" "test" { + count = 2 + availability_zone = data.aws_availability_zones.available.names[count.index] + cidr_block = "10.0.${count.index}.0/24" + vpc_id = aws_vpc.test.id } resource "aws_appstream_fleet" "test" { @@ -381,14 +379,27 @@ resource "aws_appstream_fleet" "test" { max_user_duration_in_seconds = 1000 vpc_config { - subnet_ids = [aws_subnet.example.id] + subnet_ids = aws_subnet.test.*.id } } -`, name, description, fleetType, instanceType, displayName) +`, name, description, fleetType, instanceType, displayName)) } func testAccAwsAppStreamFleetConfigWithTags(name, description, fleetType, instanceType, displayName string) string { - return fmt.Sprintf(` + return composeConfig( + testAccAvailableAZsNoOptInConfig(), + fmt.Sprintf(` +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_subnet" "test" { + count = 2 + availability_zone = data.aws_availability_zones.available.names[count.index] + cidr_block = "10.0.${count.index}.0/24" + vpc_id = aws_vpc.test.id +} + resource "aws_appstream_fleet" "test" { name = %[1]q image_name = "Amazon-AppStream2-Sample-Image-02-04-2019" @@ -408,6 +419,10 @@ resource "aws_appstream_fleet" "test" { tags = { Key = "value" } + + vpc_config { + subnet_ids = aws_subnet.test.*.id + } } -`, name, description, fleetType, instanceType, displayName) +`, name, description, fleetType, instanceType, displayName)) } From db6f9a74d91e1bb94342e7a7083070a6d8840f5a Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Wed, 22 Sep 2021 15:43:33 -0400 Subject: [PATCH 26/28] docs/secretsmanager/secret: Update link --- website/docs/r/secretsmanager_secret.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/secretsmanager_secret.html.markdown b/website/docs/r/secretsmanager_secret.html.markdown index 3b639f82754..e0d68242241 100644 --- a/website/docs/r/secretsmanager_secret.html.markdown +++ b/website/docs/r/secretsmanager_secret.html.markdown @@ -22,7 +22,7 @@ resource "aws_secretsmanager_secret" "example" { ### Rotation Configuration -To enable automatic secret rotation, the Secrets Manager service requires usage of a Lambda function. The [Rotate Secrets section in the Secrets Manager User Guide](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets.html) provides additional information about deploying a prebuilt Lambda functions for supported credential rotation (e.g. RDS) or deploying a custom Lambda function. +To enable automatic secret rotation, the Secrets Manager service requires usage of a Lambda function. The [Rotate Secrets section in the Secrets Manager User Guide](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets_strategies.html) provides additional information about deploying a prebuilt Lambda functions for supported credential rotation (e.g. RDS) or deploying a custom Lambda function. ~> **NOTE:** Configuring rotation causes the secret to rotate once as soon as you store the secret. Before you do this, you must ensure that all of your applications that use the credentials stored in the secret are updated to retrieve the secret from AWS Secrets Manager. The old credentials might no longer be usable after the initial rotation and any applications that you fail to update will break as soon as the old credentials are no longer valid. From 8c195d04bd60b1558add2697f1415fb0c5ff9f08 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Wed, 22 Sep 2021 15:57:36 -0400 Subject: [PATCH 27/28] docs/secretsmanager/secret: Update link --- website/docs/r/secretsmanager_secret_rotation.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/secretsmanager_secret_rotation.html.markdown b/website/docs/r/secretsmanager_secret_rotation.html.markdown index 307966364dd..4b3bb054628 100644 --- a/website/docs/r/secretsmanager_secret_rotation.html.markdown +++ b/website/docs/r/secretsmanager_secret_rotation.html.markdown @@ -27,7 +27,7 @@ resource "aws_secretsmanager_secret_rotation" "example" { ### Rotation Configuration -To enable automatic secret rotation, the Secrets Manager service requires usage of a Lambda function. The [Rotate Secrets section in the Secrets Manager User Guide](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets.html) provides additional information about deploying a prebuilt Lambda functions for supported credential rotation (e.g. RDS) or deploying a custom Lambda function. +To enable automatic secret rotation, the Secrets Manager service requires usage of a Lambda function. The [Rotate Secrets section in the Secrets Manager User Guide](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets_strategies.html) provides additional information about deploying a prebuilt Lambda functions for supported credential rotation (e.g. RDS) or deploying a custom Lambda function. ~> **NOTE:** Configuring rotation causes the secret to rotate once as soon as you enable rotation. Before you do this, you must ensure that all of your applications that use the credentials stored in the secret are updated to retrieve the secret from AWS Secrets Manager. The old credentials might no longer be usable after the initial rotation and any applications that you fail to update will break as soon as the old credentials are no longer valid. From 2e321d827963199e0baf554ded94a106900f97a0 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Wed, 22 Sep 2021 16:14:58 -0400 Subject: [PATCH 28/28] Fix overlapping names --- aws/data_source_aws_iam_group_test.go | 8 ++++---- aws/data_source_aws_iam_role_test.go | 8 ++++---- aws/resource_aws_codestarconnections_host_test.go | 8 +++++--- aws/resource_aws_sqs_queue_policy_test.go | 12 ++++++------ 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/aws/data_source_aws_iam_group_test.go b/aws/data_source_aws_iam_group_test.go index 1ac35b1bb51..6fb013ccbb0 100644 --- a/aws/data_source_aws_iam_group_test.go +++ b/aws/data_source_aws_iam_group_test.go @@ -18,7 +18,7 @@ func TestAccAWSDataSourceIAMGroup_basic(t *testing.T) { Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testAccAwsIAMGroupConfig(groupName), + Config: testAccAwsIAMGroupDataSourceConfig(groupName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("data.aws_iam_group.test", "group_id"), resource.TestCheckResourceAttr("data.aws_iam_group.test", "path", "/"), @@ -42,7 +42,7 @@ func TestAccAWSDataSourceIAMGroup_users(t *testing.T) { Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testAccAwsIAMGroupConfigWithUser(groupName, userName, groupMemberShipName, userCount), + Config: testAccAwsIAMGroupDataSourceConfigWithUser(groupName, userName, groupMemberShipName, userCount), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("data.aws_iam_group.test", "group_id"), resource.TestCheckResourceAttr("data.aws_iam_group.test", "path", "/"), @@ -59,7 +59,7 @@ func TestAccAWSDataSourceIAMGroup_users(t *testing.T) { }) } -func testAccAwsIAMGroupConfig(name string) string { +func testAccAwsIAMGroupDataSourceConfig(name string) string { return fmt.Sprintf(` resource "aws_iam_group" "group" { name = "%s" @@ -72,7 +72,7 @@ data "aws_iam_group" "test" { `, name) } -func testAccAwsIAMGroupConfigWithUser(groupName, userName, membershipName string, userCount int) string { +func testAccAwsIAMGroupDataSourceConfigWithUser(groupName, userName, membershipName string, userCount int) string { return fmt.Sprintf(` resource "aws_iam_group" "group" { name = "%s" diff --git a/aws/data_source_aws_iam_role_test.go b/aws/data_source_aws_iam_role_test.go index b4525df232e..6d93568c040 100644 --- a/aws/data_source_aws_iam_role_test.go +++ b/aws/data_source_aws_iam_role_test.go @@ -20,7 +20,7 @@ func TestAccAWSDataSourceIAMRole_basic(t *testing.T) { Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testAccAwsIAMRoleConfig(roleName), + Config: testAccAwsIAMRoleDataSourceConfig(roleName), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrPair(dataSourceName, "arn", resourceName, "arn"), resource.TestCheckResourceAttrPair(dataSourceName, "assume_role_policy", resourceName, "assume_role_policy"), @@ -48,7 +48,7 @@ func TestAccAWSDataSourceIAMRole_tags(t *testing.T) { Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testAccAwsIAMRoleConfig_tags(roleName), + Config: testAccAwsIAMRoleDataSourceConfig_tags(roleName), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrPair(dataSourceName, "arn", resourceName, "arn"), resource.TestCheckResourceAttrPair(dataSourceName, "assume_role_policy", resourceName, "assume_role_policy"), @@ -67,7 +67,7 @@ func TestAccAWSDataSourceIAMRole_tags(t *testing.T) { }) } -func testAccAwsIAMRoleConfig(roleName string) string { +func testAccAwsIAMRoleDataSourceConfig(roleName string) string { return fmt.Sprintf(` resource "aws_iam_role" "test" { name = %[1]q @@ -97,7 +97,7 @@ data "aws_iam_role" "test" { `, roleName) } -func testAccAwsIAMRoleConfig_tags(roleName string) string { +func testAccAwsIAMRoleDataSourceConfig_tags(roleName string) string { return fmt.Sprintf(` resource "aws_iam_role" "test" { name = %q diff --git a/aws/resource_aws_codestarconnections_host_test.go b/aws/resource_aws_codestarconnections_host_test.go index 923594de423..46ecb06b571 100644 --- a/aws/resource_aws_codestarconnections_host_test.go +++ b/aws/resource_aws_codestarconnections_host_test.go @@ -148,7 +148,7 @@ func testAccCheckAWSCodeStarConnectionsHostDestroy(s *terraform.State) error { return nil } -func testAccAWSCodeStarConnectionsHostVpcConfig(rName string) string { +func testAccAWSCodeStarConnectionsHostVpcBaseConfig(rName string) string { return fmt.Sprintf(` data "aws_availability_zones" "available" { state = "available" @@ -209,7 +209,9 @@ resource "aws_codestarconnections_host" "test" { } func testAccAWSCodeStarConnectionsHostConfigVpcConfig(rName string) string { - return testAccAWSCodeStarConnectionsHostVpcConfig(rName) + fmt.Sprintf(` + return composeConfig( + testAccAWSCodeStarConnectionsHostVpcBaseConfig(rName), + fmt.Sprintf(` resource "aws_codestarconnections_host" "test" { name = %[1]q provider_endpoint = "https://test.com" @@ -221,5 +223,5 @@ resource "aws_codestarconnections_host" "test" { vpc_id = aws_vpc.test.id } } -`, rName) +`, rName)) } diff --git a/aws/resource_aws_sqs_queue_policy_test.go b/aws/resource_aws_sqs_queue_policy_test.go index 1f24a156b26..45254ff0af0 100644 --- a/aws/resource_aws_sqs_queue_policy_test.go +++ b/aws/resource_aws_sqs_queue_policy_test.go @@ -22,7 +22,7 @@ func TestAccAWSSQSQueuePolicy_basic(t *testing.T) { CheckDestroy: testAccCheckAWSSQSQueueDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSSQSPolicyConfig(rName), + Config: testAccAWSSQSQueuePolicyConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSSQSQueueExists(queueResourceName, &queueAttributes), resource.TestCheckResourceAttrSet(resourceName, "policy"), @@ -34,7 +34,7 @@ func TestAccAWSSQSQueuePolicy_basic(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccAWSSQSPolicyConfig(rName), + Config: testAccAWSSQSQueuePolicyConfig(rName), PlanOnly: true, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair(resourceName, "policy", queueResourceName, "policy"), @@ -57,7 +57,7 @@ func TestAccAWSSQSQueuePolicy_disappears(t *testing.T) { CheckDestroy: testAccCheckAWSSQSQueueDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSSQSPolicyConfig(rName), + Config: testAccAWSSQSQueuePolicyConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSSQSQueueExists(queueResourceName, &queueAttributes), testAccCheckResourceDisappears(testAccProvider, resourceAwsSqsQueuePolicy(), resourceName), @@ -80,7 +80,7 @@ func TestAccAWSSQSQueuePolicy_disappears_queue(t *testing.T) { CheckDestroy: testAccCheckAWSSQSQueueDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSSQSPolicyConfig(rName), + Config: testAccAWSSQSQueuePolicyConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSSQSQueueExists(queueResourceName, &queueAttributes), testAccCheckResourceDisappears(testAccProvider, resourceAwsSqsQueue(), queueResourceName), @@ -104,7 +104,7 @@ func TestAccAWSSQSQueuePolicy_Update(t *testing.T) { CheckDestroy: testAccCheckAWSSQSQueueDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSSQSPolicyConfig(rName), + Config: testAccAWSSQSQueuePolicyConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSSQSQueueExists(queueResourceName, &queueAttributes), resource.TestCheckResourceAttrSet(resourceName, "policy"), @@ -125,7 +125,7 @@ func TestAccAWSSQSQueuePolicy_Update(t *testing.T) { }) } -func testAccAWSSQSPolicyConfig(rName string) string { +func testAccAWSSQSQueuePolicyConfig(rName string) string { return fmt.Sprintf(` resource "aws_sqs_queue" "test" { name = %[1]q